Auto merge of #69172 - JohnTitor:rollup-6cbmwcw, r=JohnTitor
Rollup of 7 pull requests Successful merges: - #68129 (Correct inference of primitive operand type behind binary operation) - #68475 (Use a `ParamEnvAnd<Predicate>` for caching in `ObligationForest`) - #68856 (typeck: clarify def_bm adjustments & add tests for or-patterns) - #69051 (simplify_try: address some of eddyb's comments) - #69128 (Fix extra subslice lowering) - #69150 (Follow-up to #68848) - #69164 (Update pulldown-cmark dependency) Failed merges: r? @ghost
This commit is contained in:
commit
b92c6ee882
42
Cargo.lock
42
Cargo.lock
@ -1564,7 +1564,7 @@ dependencies = [
|
||||
"rand_xoshiro",
|
||||
"sized-chunks",
|
||||
"typenum",
|
||||
"version_check 0.9.1",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2014,9 +2014,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.2.0"
|
||||
version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39"
|
||||
checksum = "53445de381a1f436797497c61d851644d0e8e88e6140f22872ad33a704933978"
|
||||
|
||||
[[package]]
|
||||
name = "memmap"
|
||||
@ -2602,17 +2602,6 @@ dependencies = [
|
||||
"url 2.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pulldown-cmark"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77043da1282374688ee212dc44b3f37ff929431de9c9adc3053bd3cee5630357"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"memchr",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pulldown-cmark"
|
||||
version = "0.6.1"
|
||||
@ -2625,6 +2614,17 @@ dependencies = [
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pulldown-cmark"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c2d7fd131800e0d63df52aff46201acaab70b431a4a1ec6f0343fe8e64f35a4"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"memchr",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "punycode"
|
||||
version = "0.4.0"
|
||||
@ -4160,7 +4160,7 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"itertools 0.8.0",
|
||||
"minifier",
|
||||
"pulldown-cmark 0.5.3",
|
||||
"pulldown-cmark 0.7.0",
|
||||
"rustc-rayon",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -5160,11 +5160,11 @@ checksum = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.5.1"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e2e6bd1e59e56598518beb94fd6db628ded570326f0a98c679a304bd9f00150"
|
||||
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
|
||||
dependencies = [
|
||||
"version_check 0.1.5",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5334,12 +5334,6 @@ dependencies = [
|
||||
"failure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.1"
|
||||
|
@ -18,10 +18,13 @@ use super::{FulfillmentError, FulfillmentErrorCode};
|
||||
use super::{ObligationCause, PredicateObligation};
|
||||
|
||||
impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> {
|
||||
type Predicate = ty::Predicate<'tcx>;
|
||||
/// Note that we include both the `ParamEnv` and the `Predicate`,
|
||||
/// as the `ParamEnv` can influence whether fulfillment succeeds
|
||||
/// or fails.
|
||||
type CacheKey = ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>;
|
||||
|
||||
fn as_predicate(&self) -> &Self::Predicate {
|
||||
&self.obligation.predicate
|
||||
fn as_cache_key(&self) -> Self::CacheKey {
|
||||
self.obligation.param_env.and(self.obligation.predicate)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,6 +128,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
let mut slice = None;
|
||||
let mut prev_rest_span = None;
|
||||
|
||||
// Lowers `$bm $ident @ ..` to `$bm $ident @ _`.
|
||||
let lower_rest_sub = |this: &mut Self, pat, bm, ident, sub| {
|
||||
let lower_sub = |this: &mut Self| Some(this.pat_wild_with_node_id_of(sub));
|
||||
let node = this.lower_pat_ident(pat, bm, ident, lower_sub);
|
||||
this.pat_with_node_id_of(pat, node)
|
||||
};
|
||||
|
||||
let mut iter = pats.iter();
|
||||
// Lower all the patterns until the first occurrence of a sub-slice pattern.
|
||||
for pat in iter.by_ref() {
|
||||
@ -142,9 +149,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
// Record, lower it to `$binding_mode $ident @ _`, and stop here.
|
||||
PatKind::Ident(ref bm, ident, Some(ref sub)) if sub.is_rest() => {
|
||||
prev_rest_span = Some(sub.span);
|
||||
let lower_sub = |this: &mut Self| Some(this.pat_wild_with_node_id_of(sub));
|
||||
let node = self.lower_pat_ident(pat, bm, ident, lower_sub);
|
||||
slice = Some(self.pat_with_node_id_of(pat, node));
|
||||
slice = Some(lower_rest_sub(self, pat, bm, ident, sub));
|
||||
break;
|
||||
}
|
||||
// It was not a subslice pattern so lower it normally.
|
||||
@ -157,9 +162,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
// There was a previous subslice pattern; make sure we don't allow more.
|
||||
let rest_span = match pat.kind {
|
||||
PatKind::Rest => Some(pat.span),
|
||||
PatKind::Ident(.., Some(ref sub)) if sub.is_rest() => {
|
||||
// The `HirValidator` is merciless; add a `_` pattern to avoid ICEs.
|
||||
after.push(self.pat_wild_with_node_id_of(pat));
|
||||
PatKind::Ident(ref bm, ident, Some(ref sub)) if sub.is_rest() => {
|
||||
// #69103: Lower into `binding @ _` as above to avoid ICEs.
|
||||
after.push(lower_rest_sub(self, pat, bm, ident, sub));
|
||||
Some(sub.span)
|
||||
}
|
||||
_ => None,
|
||||
|
@ -52,7 +52,7 @@ impl<'a, O: ForestObligation + 'a> dot::Labeller<'a> for &'a ObligationForest<O>
|
||||
|
||||
fn node_label(&self, index: &Self::Node) -> dot::LabelText<'_> {
|
||||
let node = &self.nodes[*index];
|
||||
let label = format!("{:?} ({:?})", node.obligation.as_predicate(), node.state.get());
|
||||
let label = format!("{:?} ({:?})", node.obligation.as_cache_key(), node.state.get());
|
||||
|
||||
dot::LabelText::LabelStr(label.into())
|
||||
}
|
||||
|
@ -86,9 +86,13 @@ mod graphviz;
|
||||
mod tests;
|
||||
|
||||
pub trait ForestObligation: Clone + Debug {
|
||||
type Predicate: Clone + hash::Hash + Eq + Debug;
|
||||
type CacheKey: Clone + hash::Hash + Eq + Debug;
|
||||
|
||||
fn as_predicate(&self) -> &Self::Predicate;
|
||||
/// Converts this `ForestObligation` suitable for use as a cache key.
|
||||
/// If two distinct `ForestObligations`s return the same cache key,
|
||||
/// then it must be sound to use the result of processing one obligation
|
||||
/// (e.g. success for error) for the other obligation
|
||||
fn as_cache_key(&self) -> Self::CacheKey;
|
||||
}
|
||||
|
||||
pub trait ObligationProcessor {
|
||||
@ -138,12 +142,12 @@ pub struct ObligationForest<O: ForestObligation> {
|
||||
nodes: Vec<Node<O>>,
|
||||
|
||||
/// A cache of predicates that have been successfully completed.
|
||||
done_cache: FxHashSet<O::Predicate>,
|
||||
done_cache: FxHashSet<O::CacheKey>,
|
||||
|
||||
/// A cache of the nodes in `nodes`, indexed by predicate. Unfortunately,
|
||||
/// its contents are not guaranteed to match those of `nodes`. See the
|
||||
/// comments in `process_obligation` for details.
|
||||
active_cache: FxHashMap<O::Predicate, usize>,
|
||||
active_cache: FxHashMap<O::CacheKey, usize>,
|
||||
|
||||
/// A vector reused in compress(), to avoid allocating new vectors.
|
||||
node_rewrites: Vec<usize>,
|
||||
@ -157,7 +161,7 @@ pub struct ObligationForest<O: ForestObligation> {
|
||||
/// See [this][details] for details.
|
||||
///
|
||||
/// [details]: https://github.com/rust-lang/rust/pull/53255#issuecomment-421184780
|
||||
error_cache: FxHashMap<ObligationTreeId, FxHashSet<O::Predicate>>,
|
||||
error_cache: FxHashMap<ObligationTreeId, FxHashSet<O::CacheKey>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -305,11 +309,12 @@ impl<O: ForestObligation> ObligationForest<O> {
|
||||
|
||||
// Returns Err(()) if we already know this obligation failed.
|
||||
fn register_obligation_at(&mut self, obligation: O, parent: Option<usize>) -> Result<(), ()> {
|
||||
if self.done_cache.contains(obligation.as_predicate()) {
|
||||
if self.done_cache.contains(&obligation.as_cache_key()) {
|
||||
debug!("register_obligation_at: ignoring already done obligation: {:?}", obligation);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match self.active_cache.entry(obligation.as_predicate().clone()) {
|
||||
match self.active_cache.entry(obligation.as_cache_key().clone()) {
|
||||
Entry::Occupied(o) => {
|
||||
let node = &mut self.nodes[*o.get()];
|
||||
if let Some(parent_index) = parent {
|
||||
@ -333,7 +338,7 @@ impl<O: ForestObligation> ObligationForest<O> {
|
||||
&& self
|
||||
.error_cache
|
||||
.get(&obligation_tree_id)
|
||||
.map(|errors| errors.contains(obligation.as_predicate()))
|
||||
.map(|errors| errors.contains(&obligation.as_cache_key()))
|
||||
.unwrap_or(false);
|
||||
|
||||
if already_failed {
|
||||
@ -380,7 +385,7 @@ impl<O: ForestObligation> ObligationForest<O> {
|
||||
self.error_cache
|
||||
.entry(node.obligation_tree_id)
|
||||
.or_default()
|
||||
.insert(node.obligation.as_predicate().clone());
|
||||
.insert(node.obligation.as_cache_key().clone());
|
||||
}
|
||||
|
||||
/// Performs a pass through the obligation list. This must
|
||||
@ -618,11 +623,11 @@ impl<O: ForestObligation> ObligationForest<O> {
|
||||
// `self.nodes`. See the comment in `process_obligation`
|
||||
// for more details.
|
||||
if let Some((predicate, _)) =
|
||||
self.active_cache.remove_entry(node.obligation.as_predicate())
|
||||
self.active_cache.remove_entry(&node.obligation.as_cache_key())
|
||||
{
|
||||
self.done_cache.insert(predicate);
|
||||
} else {
|
||||
self.done_cache.insert(node.obligation.as_predicate().clone());
|
||||
self.done_cache.insert(node.obligation.as_cache_key().clone());
|
||||
}
|
||||
if do_completed == DoCompleted::Yes {
|
||||
// Extract the success stories.
|
||||
@ -635,7 +640,7 @@ impl<O: ForestObligation> ObligationForest<O> {
|
||||
// We *intentionally* remove the node from the cache at this point. Otherwise
|
||||
// tests must come up with a different type on every type error they
|
||||
// check against.
|
||||
self.active_cache.remove(node.obligation.as_predicate());
|
||||
self.active_cache.remove(&node.obligation.as_cache_key());
|
||||
self.insert_into_error_cache(index);
|
||||
node_rewrites[index] = orig_nodes_len;
|
||||
dead_nodes += 1;
|
||||
|
@ -4,9 +4,9 @@ use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
impl<'a> super::ForestObligation for &'a str {
|
||||
type Predicate = &'a str;
|
||||
type CacheKey = &'a str;
|
||||
|
||||
fn as_predicate(&self) -> &Self::Predicate {
|
||||
fn as_cache_key(&self) -> Self::CacheKey {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -191,9 +191,9 @@ fn generic_extension<'cx>(
|
||||
let mut best_failure: Option<(Token, &str)> = None;
|
||||
|
||||
// We create a base parser that can be used for the "black box" parts.
|
||||
// Every iteration needs a fresh copy of that base parser. However, the
|
||||
// parser is not mutated on many of the iterations, particularly when
|
||||
// dealing with macros like this:
|
||||
// Every iteration needs a fresh copy of that parser. However, the parser
|
||||
// is not mutated on many of the iterations, particularly when dealing with
|
||||
// macros like this:
|
||||
//
|
||||
// macro_rules! foo {
|
||||
// ("a") => (A);
|
||||
@ -209,11 +209,9 @@ fn generic_extension<'cx>(
|
||||
// hacky, but speeds up the `html5ever` benchmark significantly. (Issue
|
||||
// 68836 suggests a more comprehensive but more complex change to deal with
|
||||
// this situation.)
|
||||
let base_parser = base_parser_from_cx(&cx.current_expansion, &cx.parse_sess, arg.clone());
|
||||
let parser = parser_from_cx(&cx.current_expansion, &cx.parse_sess, arg.clone());
|
||||
|
||||
for (i, lhs) in lhses.iter().enumerate() {
|
||||
let mut parser = Cow::Borrowed(&base_parser);
|
||||
|
||||
// try each arm's matchers
|
||||
let lhs_tt = match *lhs {
|
||||
mbe::TokenTree::Delimited(_, ref delim) => &delim.tts[..],
|
||||
@ -224,13 +222,14 @@ fn generic_extension<'cx>(
|
||||
// This is used so that if a matcher is not `Success(..)`ful,
|
||||
// then the spans which became gated when parsing the unsuccessful matcher
|
||||
// are not recorded. On the first `Success(..)`ful matcher, the spans are merged.
|
||||
let mut gated_spans_snaphot = mem::take(&mut *cx.parse_sess.gated_spans.spans.borrow_mut());
|
||||
let mut gated_spans_snapshot =
|
||||
mem::take(&mut *cx.parse_sess.gated_spans.spans.borrow_mut());
|
||||
|
||||
match parse_tt(&mut parser, lhs_tt) {
|
||||
match parse_tt(&mut Cow::Borrowed(&parser), lhs_tt) {
|
||||
Success(named_matches) => {
|
||||
// The matcher was `Success(..)`ful.
|
||||
// Merge the gated spans from parsing the matcher with the pre-existing ones.
|
||||
cx.parse_sess.gated_spans.merge(gated_spans_snaphot);
|
||||
cx.parse_sess.gated_spans.merge(gated_spans_snapshot);
|
||||
|
||||
let rhs = match rhses[i] {
|
||||
// ignore delimiters
|
||||
@ -291,9 +290,9 @@ fn generic_extension<'cx>(
|
||||
|
||||
// The matcher was not `Success(..)`ful.
|
||||
// Restore to the state before snapshotting and maybe try again.
|
||||
mem::swap(&mut gated_spans_snaphot, &mut cx.parse_sess.gated_spans.spans.borrow_mut());
|
||||
mem::swap(&mut gated_spans_snapshot, &mut cx.parse_sess.gated_spans.spans.borrow_mut());
|
||||
}
|
||||
drop(base_parser);
|
||||
drop(parser);
|
||||
|
||||
let (token, label) = best_failure.expect("ran no matchers");
|
||||
let span = token.span.substitute_dummy(sp);
|
||||
@ -311,9 +310,8 @@ fn generic_extension<'cx>(
|
||||
mbe::TokenTree::Delimited(_, ref delim) => &delim.tts[..],
|
||||
_ => continue,
|
||||
};
|
||||
let base_parser =
|
||||
base_parser_from_cx(&cx.current_expansion, &cx.parse_sess, arg.clone());
|
||||
match parse_tt(&mut Cow::Borrowed(&base_parser), lhs_tt) {
|
||||
let parser = parser_from_cx(&cx.current_expansion, &cx.parse_sess, arg.clone());
|
||||
match parse_tt(&mut Cow::Borrowed(&parser), lhs_tt) {
|
||||
Success(_) => {
|
||||
if comma_span.is_dummy() {
|
||||
err.note("you might be missing a comma");
|
||||
@ -395,8 +393,8 @@ pub fn compile_declarative_macro(
|
||||
),
|
||||
];
|
||||
|
||||
let base_parser = Parser::new(sess, body, None, true, true, rustc_parse::MACRO_ARGUMENTS);
|
||||
let argument_map = match parse_tt(&mut Cow::Borrowed(&base_parser), &argument_gram) {
|
||||
let parser = Parser::new(sess, body, None, true, true, rustc_parse::MACRO_ARGUMENTS);
|
||||
let argument_map = match parse_tt(&mut Cow::Borrowed(&parser), &argument_gram) {
|
||||
Success(m) => m,
|
||||
Failure(token, msg) => {
|
||||
let s = parse_failure_msg(&token);
|
||||
@ -1212,7 +1210,7 @@ fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
fn base_parser_from_cx<'cx>(
|
||||
fn parser_from_cx<'cx>(
|
||||
current_expansion: &'cx ExpansionData,
|
||||
sess: &'cx ParseSess,
|
||||
tts: TokenStream,
|
||||
|
@ -52,6 +52,8 @@ impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity {
|
||||
Some(x) => x,
|
||||
};
|
||||
if local_tmp_s0 != local_tmp_s1
|
||||
// Avoid moving into ourselves.
|
||||
|| local_0 == local_1
|
||||
// The field-and-variant information match up.
|
||||
|| vf_s0 != vf_s1
|
||||
// Source and target locals have the same type.
|
||||
@ -64,6 +66,7 @@ impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity {
|
||||
}
|
||||
|
||||
// Right shape; transform!
|
||||
s0.source_info = s2.source_info;
|
||||
match &mut s0.kind {
|
||||
StatementKind::Assign(box (place, rvalue)) => {
|
||||
*place = local_0.into();
|
||||
|
@ -25,7 +25,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
||||
let ty =
|
||||
if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
|
||||
self.enforce_builtin_binop_types(lhs, lhs_ty, rhs, rhs_ty, op);
|
||||
self.enforce_builtin_binop_types(&lhs.span, lhs_ty, &rhs.span, rhs_ty, op);
|
||||
self.tcx.mk_unit()
|
||||
} else {
|
||||
return_ty
|
||||
@ -86,8 +86,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
&& !rhs_ty.is_ty_var()
|
||||
&& is_builtin_binop(lhs_ty, rhs_ty, op)
|
||||
{
|
||||
let builtin_return_ty =
|
||||
self.enforce_builtin_binop_types(lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
|
||||
let builtin_return_ty = self.enforce_builtin_binop_types(
|
||||
&lhs_expr.span,
|
||||
lhs_ty,
|
||||
&rhs_expr.span,
|
||||
rhs_ty,
|
||||
op,
|
||||
);
|
||||
self.demand_suptype(expr.span, builtin_return_ty, return_ty);
|
||||
}
|
||||
|
||||
@ -98,19 +103,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
||||
fn enforce_builtin_binop_types(
|
||||
&self,
|
||||
lhs_expr: &'tcx hir::Expr<'tcx>,
|
||||
lhs_span: &Span,
|
||||
lhs_ty: Ty<'tcx>,
|
||||
rhs_expr: &'tcx hir::Expr<'tcx>,
|
||||
rhs_span: &Span,
|
||||
rhs_ty: Ty<'tcx>,
|
||||
op: hir::BinOp,
|
||||
) -> Ty<'tcx> {
|
||||
debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op));
|
||||
|
||||
// Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work.
|
||||
// (See https://github.com/rust-lang/rust/issues/57447.)
|
||||
let (lhs_ty, rhs_ty) = (deref_ty_if_possible(lhs_ty), deref_ty_if_possible(rhs_ty));
|
||||
|
||||
let tcx = self.tcx;
|
||||
match BinOpCategory::from(op) {
|
||||
BinOpCategory::Shortcircuit => {
|
||||
self.demand_suptype(lhs_expr.span, tcx.mk_bool(), lhs_ty);
|
||||
self.demand_suptype(rhs_expr.span, tcx.mk_bool(), rhs_ty);
|
||||
self.demand_suptype(*lhs_span, tcx.mk_bool(), lhs_ty);
|
||||
self.demand_suptype(*rhs_span, tcx.mk_bool(), rhs_ty);
|
||||
tcx.mk_bool()
|
||||
}
|
||||
|
||||
@ -121,13 +130,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
||||
BinOpCategory::Math | BinOpCategory::Bitwise => {
|
||||
// both LHS and RHS and result will have the same type
|
||||
self.demand_suptype(rhs_expr.span, lhs_ty, rhs_ty);
|
||||
self.demand_suptype(*rhs_span, lhs_ty, rhs_ty);
|
||||
lhs_ty
|
||||
}
|
||||
|
||||
BinOpCategory::Comparison => {
|
||||
// both LHS and RHS and result will have the same type
|
||||
self.demand_suptype(rhs_expr.span, lhs_ty, rhs_ty);
|
||||
self.demand_suptype(*rhs_span, lhs_ty, rhs_ty);
|
||||
tcx.mk_bool()
|
||||
}
|
||||
}
|
||||
@ -862,6 +871,14 @@ enum Op {
|
||||
Unary(hir::UnOp, Span),
|
||||
}
|
||||
|
||||
/// Dereferences a single level of immutable referencing.
|
||||
fn deref_ty_if_possible<'tcx>(ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
match ty.kind {
|
||||
ty::Ref(_, ty, hir::Mutability::Not) => ty,
|
||||
_ => ty,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if this is a built-in arithmetic operation (e.g., u32
|
||||
/// + u32, i16x4 == i16x4) and false if these types would have to be
|
||||
/// overloaded to be legal. There are two reasons that we distinguish
|
||||
@ -878,7 +895,11 @@ enum Op {
|
||||
/// Reason #2 is the killer. I tried for a while to always use
|
||||
/// overloaded logic and just check the types in constants/codegen after
|
||||
/// the fact, and it worked fine, except for SIMD types. -nmatsakis
|
||||
fn is_builtin_binop(lhs: Ty<'_>, rhs: Ty<'_>, op: hir::BinOp) -> bool {
|
||||
fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool {
|
||||
// Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work.
|
||||
// (See https://github.com/rust-lang/rust/issues/57447.)
|
||||
let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs));
|
||||
|
||||
match BinOpCategory::from(op) {
|
||||
BinOpCategory::Shortcircuit => true,
|
||||
|
||||
|
@ -89,6 +89,18 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
const INITIAL_BM: BindingMode = BindingMode::BindByValue(hir::Mutability::Not);
|
||||
|
||||
/// Mode for adjusting the expected type and binding mode.
|
||||
enum AdjustMode {
|
||||
/// Peel off all immediate reference types.
|
||||
Peel,
|
||||
/// Reset binding mode to the inital mode.
|
||||
Reset,
|
||||
/// Pass on the input binding mode and expected type.
|
||||
Pass,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
/// Type check the given top level pattern against the `expected` type.
|
||||
///
|
||||
@ -105,8 +117,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
span: Option<Span>,
|
||||
origin_expr: bool,
|
||||
) {
|
||||
let def_bm = BindingMode::BindByValue(hir::Mutability::Not);
|
||||
self.check_pat(pat, expected, def_bm, TopInfo { expected, origin_expr, span });
|
||||
self.check_pat(pat, expected, INITIAL_BM, TopInfo { expected, origin_expr, span });
|
||||
}
|
||||
|
||||
/// Type check the given `pat` against the `expected` type
|
||||
@ -123,12 +134,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
) {
|
||||
debug!("check_pat(pat={:?},expected={:?},def_bm={:?})", pat, expected, def_bm);
|
||||
|
||||
let path_resolution = match &pat.kind {
|
||||
let path_res = match &pat.kind {
|
||||
PatKind::Path(qpath) => Some(self.resolve_ty_and_res_ufcs(qpath, pat.hir_id, pat.span)),
|
||||
_ => None,
|
||||
};
|
||||
let is_nrp = self.is_non_ref_pat(pat, path_resolution.map(|(res, ..)| res));
|
||||
let (expected, def_bm) = self.calc_default_binding_mode(pat, expected, def_bm, is_nrp);
|
||||
let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res));
|
||||
let (expected, def_bm) = self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode);
|
||||
|
||||
let ty = match pat.kind {
|
||||
PatKind::Wild => expected,
|
||||
@ -141,7 +152,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, def_bm, ti)
|
||||
}
|
||||
PatKind::Path(ref qpath) => {
|
||||
self.check_pat_path(pat, path_resolution.unwrap(), qpath, expected)
|
||||
self.check_pat_path(pat, path_res.unwrap(), qpath, expected)
|
||||
}
|
||||
PatKind::Struct(ref qpath, fields, etc) => {
|
||||
self.check_pat_struct(pat, qpath, fields, etc, expected, def_bm, ti)
|
||||
@ -223,15 +234,48 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
pat: &'tcx Pat<'tcx>,
|
||||
expected: Ty<'tcx>,
|
||||
def_bm: BindingMode,
|
||||
is_non_ref_pat: bool,
|
||||
adjust_mode: AdjustMode,
|
||||
) -> (Ty<'tcx>, BindingMode) {
|
||||
if is_non_ref_pat {
|
||||
debug!("pattern is non reference pattern");
|
||||
self.peel_off_references(pat, expected, def_bm)
|
||||
} else {
|
||||
// When you encounter a `&pat` pattern, reset to "by
|
||||
// value". This is so that `x` and `y` here are by value,
|
||||
// as they appear to be:
|
||||
match adjust_mode {
|
||||
AdjustMode::Pass => (expected, def_bm),
|
||||
AdjustMode::Reset => (expected, INITIAL_BM),
|
||||
AdjustMode::Peel => self.peel_off_references(pat, expected, def_bm),
|
||||
}
|
||||
}
|
||||
|
||||
/// How should the binding mode and expected type be adjusted?
|
||||
///
|
||||
/// When the pattern is a path pattern, `opt_path_res` must be `Some(res)`.
|
||||
fn calc_adjust_mode(&self, pat: &'tcx Pat<'tcx>, opt_path_res: Option<Res>) -> AdjustMode {
|
||||
match &pat.kind {
|
||||
// Type checking these product-like types successfully always require
|
||||
// that the expected type be of those types and not reference types.
|
||||
PatKind::Struct(..)
|
||||
| PatKind::TupleStruct(..)
|
||||
| PatKind::Tuple(..)
|
||||
| PatKind::Box(_)
|
||||
| PatKind::Range(..)
|
||||
| PatKind::Slice(..) => AdjustMode::Peel,
|
||||
// String and byte-string literals result in types `&str` and `&[u8]` respectively.
|
||||
// All other literals result in non-reference types.
|
||||
// As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo {}`.
|
||||
PatKind::Lit(lt) => match self.check_expr(lt).kind {
|
||||
ty::Ref(..) => AdjustMode::Pass,
|
||||
_ => AdjustMode::Peel,
|
||||
},
|
||||
PatKind::Path(_) => match opt_path_res.unwrap() {
|
||||
// These constants can be of a reference type, e.g. `const X: &u8 = &0;`.
|
||||
// Peeling the reference types too early will cause type checking failures.
|
||||
// Although it would be possible to *also* peel the types of the constants too.
|
||||
Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => AdjustMode::Pass,
|
||||
// In the `ValueNS`, we have `SelfCtor(..) | Ctor(_, Const), _)` remaining which
|
||||
// could successfully compile. The former being `Self` requires a unit struct.
|
||||
// In either case, and unlike constants, the pattern itself cannot be
|
||||
// a reference type wherefore peeling doesn't give up any expressivity.
|
||||
_ => AdjustMode::Peel,
|
||||
},
|
||||
// When encountering a `& mut? pat` pattern, reset to "by value".
|
||||
// This is so that `x` and `y` here are by value, as they appear to be:
|
||||
//
|
||||
// ```
|
||||
// match &(&22, &44) {
|
||||
@ -240,47 +284,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// ```
|
||||
//
|
||||
// See issue #46688.
|
||||
let def_bm = match pat.kind {
|
||||
PatKind::Ref(..) => ty::BindByValue(hir::Mutability::Not),
|
||||
_ => def_bm,
|
||||
};
|
||||
(expected, def_bm)
|
||||
}
|
||||
}
|
||||
|
||||
/// Is the pattern a "non reference pattern"?
|
||||
/// When the pattern is a path pattern, `opt_path_res` must be `Some(res)`.
|
||||
fn is_non_ref_pat(&self, pat: &'tcx Pat<'tcx>, opt_path_res: Option<Res>) -> bool {
|
||||
match pat.kind {
|
||||
PatKind::Struct(..)
|
||||
| PatKind::TupleStruct(..)
|
||||
| PatKind::Tuple(..)
|
||||
| PatKind::Box(_)
|
||||
| PatKind::Range(..)
|
||||
| PatKind::Slice(..) => true,
|
||||
PatKind::Lit(ref lt) => {
|
||||
let ty = self.check_expr(lt);
|
||||
match ty.kind {
|
||||
ty::Ref(..) => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
PatKind::Path(_) => match opt_path_res.unwrap() {
|
||||
Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => false,
|
||||
_ => true,
|
||||
},
|
||||
// FIXME(or_patterns; Centril | dlrobertson): To keep things compiling
|
||||
// for or-patterns at the top level, we need to make `p_0 | ... | p_n`
|
||||
// a "non reference pattern". For example the following currently compiles:
|
||||
// ```
|
||||
// match &1 {
|
||||
// e @ &(1...2) | e @ &(3...4) => {}
|
||||
// _ => {}
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// We should consider whether we should do something special in nested or-patterns.
|
||||
PatKind::Or(_) | PatKind::Wild | PatKind::Binding(..) | PatKind::Ref(..) => false,
|
||||
PatKind::Ref(..) => AdjustMode::Reset,
|
||||
// A `_` pattern works with any expected type, so there's no need to do anything.
|
||||
PatKind::Wild
|
||||
// Bindings also work with whatever the expected type is,
|
||||
// and moreover if we peel references off, that will give us the wrong binding type.
|
||||
// Also, we can have a subpattern `binding @ pat`.
|
||||
// Each side of the `@` should be treated independently (like with OR-patterns).
|
||||
| PatKind::Binding(..)
|
||||
// An OR-pattern just propagates to each individual alternative.
|
||||
// This is maximally flexible, allowing e.g., `Some(mut x) | &Some(mut x)`.
|
||||
// In that example, `Some(mut x)` results in `Peel` whereas `&Some(mut x)` in `Reset`.
|
||||
| PatKind::Or(_) => AdjustMode::Pass,
|
||||
}
|
||||
}
|
||||
|
||||
@ -508,7 +523,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let local_ty = self.local_ty(pat.span, pat.hir_id).decl_ty;
|
||||
let eq_ty = match bm {
|
||||
ty::BindByReference(mutbl) => {
|
||||
// If the binding is like `ref x | ref const x | ref mut x`
|
||||
// If the binding is like `ref x | ref mut x`,
|
||||
// then `x` is assigned a value of type `&M T` where M is the
|
||||
// mutability and T is the expected type.
|
||||
//
|
||||
|
@ -9,7 +9,7 @@ name = "rustdoc"
|
||||
path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
pulldown-cmark = { version = "0.5.3", default-features = false }
|
||||
pulldown-cmark = { version = "0.7", default-features = false }
|
||||
minifier = "0.0.33"
|
||||
rayon = { version = "0.3.0", package = "rustc-rayon" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
@ -33,7 +33,7 @@ use crate::html::highlight;
|
||||
use crate::html::toc::TocBuilder;
|
||||
use crate::test;
|
||||
|
||||
use pulldown_cmark::{html, CowStr, Event, Options, Parser, Tag};
|
||||
use pulldown_cmark::{html, CodeBlockKind, CowStr, Event, Options, Parser, Tag};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
@ -189,10 +189,15 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
|
||||
let compile_fail;
|
||||
let ignore;
|
||||
let edition;
|
||||
if let Some(Event::Start(Tag::CodeBlock(lang))) = event {
|
||||
let parse_result = LangString::parse(&lang, self.check_error_codes, false);
|
||||
if let Some(Event::Start(Tag::CodeBlock(kind))) = event {
|
||||
let parse_result = match kind {
|
||||
CodeBlockKind::Fenced(ref lang) => {
|
||||
LangString::parse(&lang, self.check_error_codes, false)
|
||||
}
|
||||
CodeBlockKind::Indented => LangString::all_false(),
|
||||
};
|
||||
if !parse_result.rust {
|
||||
return Some(Event::Start(Tag::CodeBlock(lang)));
|
||||
return Some(Event::Start(Tag::CodeBlock(kind)));
|
||||
}
|
||||
compile_fail = parse_result.compile_fail;
|
||||
ignore = parse_result.ignore;
|
||||
@ -370,11 +375,11 @@ impl<'a, 'b, 'ids, I: Iterator<Item = Event<'a>>> Iterator for HeadingLinks<'a,
|
||||
}
|
||||
|
||||
let event = self.inner.next();
|
||||
if let Some(Event::Start(Tag::Header(level))) = event {
|
||||
if let Some(Event::Start(Tag::Heading(level))) = event {
|
||||
let mut id = String::new();
|
||||
for event in &mut self.inner {
|
||||
match &event {
|
||||
Event::End(Tag::Header(..)) => break,
|
||||
Event::End(Tag::Heading(..)) => break,
|
||||
Event::Text(text) | Event::Code(text) => {
|
||||
id.extend(text.chars().filter_map(slugify));
|
||||
}
|
||||
@ -391,10 +396,10 @@ impl<'a, 'b, 'ids, I: Iterator<Item = Event<'a>>> Iterator for HeadingLinks<'a,
|
||||
let mut html_header = String::new();
|
||||
html::push_html(&mut html_header, self.buf.iter().cloned());
|
||||
let sec = builder.push(level as u32, html_header, id.clone());
|
||||
self.buf.push_front(Event::InlineHtml(format!("{} ", sec).into()));
|
||||
self.buf.push_front(Event::Html(format!("{} ", sec).into()));
|
||||
}
|
||||
|
||||
self.buf.push_back(Event::InlineHtml(format!("</a></h{}>", level).into()));
|
||||
self.buf.push_back(Event::Html(format!("</a></h{}>", level).into()));
|
||||
|
||||
let start_tags = format!(
|
||||
"<h{level} id=\"{id}\" class=\"section-header\">\
|
||||
@ -402,7 +407,7 @@ impl<'a, 'b, 'ids, I: Iterator<Item = Event<'a>>> Iterator for HeadingLinks<'a,
|
||||
id = id,
|
||||
level = level
|
||||
);
|
||||
return Some(Event::InlineHtml(start_tags.into()));
|
||||
return Some(Event::Html(start_tags.into()));
|
||||
}
|
||||
event
|
||||
}
|
||||
@ -556,40 +561,44 @@ pub fn find_testable_code<T: test::Tester>(
|
||||
error_codes: ErrorCodes,
|
||||
enable_per_target_ignores: bool,
|
||||
) {
|
||||
let mut parser = Parser::new(doc);
|
||||
let mut parser = Parser::new(doc).into_offset_iter();
|
||||
let mut prev_offset = 0;
|
||||
let mut nb_lines = 0;
|
||||
let mut register_header = None;
|
||||
while let Some(event) = parser.next() {
|
||||
while let Some((event, offset)) = parser.next() {
|
||||
match event {
|
||||
Event::Start(Tag::CodeBlock(s)) => {
|
||||
let offset = parser.get_offset();
|
||||
|
||||
let block_info = if s.is_empty() {
|
||||
LangString::all_false()
|
||||
} else {
|
||||
LangString::parse(&*s, error_codes, enable_per_target_ignores)
|
||||
Event::Start(Tag::CodeBlock(kind)) => {
|
||||
let block_info = match kind {
|
||||
CodeBlockKind::Fenced(ref lang) => {
|
||||
if lang.is_empty() {
|
||||
LangString::all_false()
|
||||
} else {
|
||||
LangString::parse(lang, error_codes, enable_per_target_ignores)
|
||||
}
|
||||
}
|
||||
CodeBlockKind::Indented => LangString::all_false(),
|
||||
};
|
||||
if !block_info.rust {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut test_s = String::new();
|
||||
|
||||
while let Some(Event::Text(s)) = parser.next() {
|
||||
while let Some((Event::Text(s), _)) = parser.next() {
|
||||
test_s.push_str(&s);
|
||||
}
|
||||
|
||||
let text = test_s
|
||||
.lines()
|
||||
.map(|l| map_line(l).for_code())
|
||||
.collect::<Vec<Cow<'_, str>>>()
|
||||
.join("\n");
|
||||
nb_lines += doc[prev_offset..offset].lines().count();
|
||||
let line = tests.get_line() + nb_lines;
|
||||
|
||||
nb_lines += doc[prev_offset..offset.start].lines().count();
|
||||
let line = tests.get_line() + nb_lines + 1;
|
||||
tests.add_test(text, block_info, line);
|
||||
prev_offset = offset;
|
||||
prev_offset = offset.start;
|
||||
}
|
||||
Event::Start(Tag::Header(level)) => {
|
||||
Event::Start(Tag::Heading(level)) => {
|
||||
register_header = Some(level as u32);
|
||||
}
|
||||
Event::Text(ref s) if register_header.is_some() => {
|
||||
@ -783,7 +792,7 @@ impl MarkdownHtml<'_> {
|
||||
|
||||
// Treat inline HTML as plain text.
|
||||
let p = p.map(|event| match event {
|
||||
Event::Html(text) | Event::InlineHtml(text) => Event::Text(text),
|
||||
Event::Html(text) => Event::Text(text),
|
||||
_ => event,
|
||||
});
|
||||
|
||||
@ -842,10 +851,10 @@ pub fn plain_summary_line(md: &str) -> String {
|
||||
let next_event = next_event.unwrap();
|
||||
let (ret, is_in) = match next_event {
|
||||
Event::Start(Tag::Paragraph) => (None, 1),
|
||||
Event::Start(Tag::Header(_)) => (None, 1),
|
||||
Event::Start(Tag::Heading(_)) => (None, 1),
|
||||
Event::Code(code) => (Some(format!("`{}`", code)), 0),
|
||||
Event::Text(ref s) if self.is_in > 0 => (Some(s.as_ref().to_owned()), 0),
|
||||
Event::End(Tag::Paragraph) | Event::End(Tag::Header(_)) => (None, -1),
|
||||
Event::End(Tag::Paragraph) | Event::End(Tag::Heading(_)) => (None, -1),
|
||||
_ => (None, 0),
|
||||
};
|
||||
if is_in > 0 || (is_in < 0 && self.is_in > 0) {
|
||||
@ -940,68 +949,79 @@ crate fn rust_code_blocks(md: &str) -> Vec<RustCodeBlock> {
|
||||
return code_blocks;
|
||||
}
|
||||
|
||||
let mut p = Parser::new_ext(md, opts());
|
||||
|
||||
let mut code_block_start = 0;
|
||||
let mut code_start = 0;
|
||||
let mut is_fenced = false;
|
||||
let mut previous_offset = 0;
|
||||
let mut in_rust_code_block = false;
|
||||
while let Some(event) = p.next() {
|
||||
let offset = p.get_offset();
|
||||
let mut p = Parser::new_ext(md, opts()).into_offset_iter();
|
||||
|
||||
while let Some((event, offset)) = p.next() {
|
||||
match event {
|
||||
Event::Start(Tag::CodeBlock(syntax)) => {
|
||||
let lang_string = if syntax.is_empty() {
|
||||
LangString::all_false()
|
||||
} else {
|
||||
LangString::parse(&*syntax, ErrorCodes::Yes, false)
|
||||
};
|
||||
|
||||
if lang_string.rust {
|
||||
in_rust_code_block = true;
|
||||
|
||||
code_start = offset;
|
||||
code_block_start = match md[previous_offset..offset].find("```") {
|
||||
Some(fence_idx) => {
|
||||
is_fenced = true;
|
||||
previous_offset + fence_idx
|
||||
let (syntax, code_start, code_end, range, is_fenced) = match syntax {
|
||||
CodeBlockKind::Fenced(syntax) => {
|
||||
let syntax = syntax.as_ref();
|
||||
let lang_string = if syntax.is_empty() {
|
||||
LangString::all_false()
|
||||
} else {
|
||||
LangString::parse(&*syntax, ErrorCodes::Yes, false)
|
||||
};
|
||||
if !lang_string.rust {
|
||||
continue;
|
||||
}
|
||||
None => {
|
||||
is_fenced = false;
|
||||
offset
|
||||
let syntax = if syntax.is_empty() { None } else { Some(syntax.to_owned()) };
|
||||
let (code_start, mut code_end) = match p.next() {
|
||||
Some((Event::Text(_), offset)) => (offset.start, offset.end),
|
||||
Some((_, sub_offset)) => {
|
||||
let code = Range { start: sub_offset.start, end: sub_offset.start };
|
||||
code_blocks.push(RustCodeBlock {
|
||||
is_fenced: true,
|
||||
range: offset,
|
||||
code,
|
||||
syntax,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
None => {
|
||||
let code = Range { start: offset.end, end: offset.end };
|
||||
code_blocks.push(RustCodeBlock {
|
||||
is_fenced: true,
|
||||
range: offset,
|
||||
code,
|
||||
syntax,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
};
|
||||
while let Some((Event::Text(_), offset)) = p.next() {
|
||||
code_end = offset.end;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
Event::End(Tag::CodeBlock(syntax)) if in_rust_code_block => {
|
||||
in_rust_code_block = false;
|
||||
|
||||
let code_block_end = if is_fenced {
|
||||
let fence_str = &md[previous_offset..offset].chars().rev().collect::<String>();
|
||||
fence_str
|
||||
.find("```")
|
||||
.map(|fence_idx| offset - fence_idx)
|
||||
.unwrap_or_else(|| offset)
|
||||
} else if md.as_bytes().get(offset).map(|b| *b == b'\n').unwrap_or_default() {
|
||||
offset - 1
|
||||
} else {
|
||||
offset
|
||||
(syntax, code_start, code_end, offset, true)
|
||||
}
|
||||
CodeBlockKind::Indented => {
|
||||
// The ending of the offset goes too far sometime so we reduce it by one in
|
||||
// these cases.
|
||||
if offset.end > offset.start
|
||||
&& md.get(offset.end..=offset.end) == Some(&"\n")
|
||||
{
|
||||
(
|
||||
None,
|
||||
offset.start,
|
||||
offset.end,
|
||||
Range { start: offset.start, end: offset.end - 1 },
|
||||
false,
|
||||
)
|
||||
} else {
|
||||
(None, offset.start, offset.end, offset, false)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let code_end = if is_fenced { previous_offset } else { code_block_end };
|
||||
|
||||
code_blocks.push(RustCodeBlock {
|
||||
is_fenced,
|
||||
range: Range { start: code_block_start, end: code_block_end },
|
||||
range,
|
||||
code: Range { start: code_start, end: code_end },
|
||||
syntax: if !syntax.is_empty() { Some(syntax.into_string()) } else { None },
|
||||
syntax,
|
||||
});
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
previous_offset = offset;
|
||||
}
|
||||
|
||||
code_blocks
|
||||
|
@ -0,0 +1,18 @@
|
||||
// We used to not lower the extra `b @ ..` into `b @ _` which meant that no type
|
||||
// was registered for the binding `b` although it passed through resolve.
|
||||
// This resulted in an ICE (#69103).
|
||||
|
||||
fn main() {
|
||||
let [a @ .., b @ ..] = &mut [1, 2];
|
||||
//~^ ERROR `..` can only be used once per slice pattern
|
||||
b;
|
||||
|
||||
let [.., c @ ..] = [1, 2];
|
||||
//~^ ERROR `..` can only be used once per slice pattern
|
||||
c;
|
||||
|
||||
// This never ICEd, but let's make sure it won't regress either.
|
||||
let (.., d @ ..) = (1, 2);
|
||||
//~^ ERROR `..` patterns are not allowed here
|
||||
d;
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
error: `..` can only be used once per slice pattern
|
||||
--> $DIR/issue-69103-extra-binding-subslice.rs:6:22
|
||||
|
|
||||
LL | let [a @ .., b @ ..] = &mut [1, 2];
|
||||
| -- ^^ can only be used once per slice pattern
|
||||
| |
|
||||
| previously used here
|
||||
|
||||
error: `..` can only be used once per slice pattern
|
||||
--> $DIR/issue-69103-extra-binding-subslice.rs:10:18
|
||||
|
|
||||
LL | let [.., c @ ..] = [1, 2];
|
||||
| -- ^^ can only be used once per slice pattern
|
||||
| |
|
||||
| previously used here
|
||||
|
||||
error: `..` patterns are not allowed here
|
||||
--> $DIR/issue-69103-extra-binding-subslice.rs:15:18
|
||||
|
|
||||
LL | let (.., d @ ..) = (1, 2);
|
||||
| ^^
|
||||
|
|
||||
= note: only allowed in tuple, tuple struct, and slice patterns
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
@ -0,0 +1,30 @@
|
||||
// check-pass
|
||||
|
||||
fn main() {
|
||||
// Test that we can infer the type of binary operands when
|
||||
// references are involved, on various types and operators.
|
||||
let _: u8 = 0 + 0;
|
||||
let _: u8 = 0 + &0;
|
||||
let _: u8 = &0 + 0;
|
||||
let _: u8 = &0 + &0;
|
||||
|
||||
let _: f32 = 0.0 + 0.0;
|
||||
let _: f32 = 0.0 + &0.0;
|
||||
let _: f32 = &0.0 + 0.0;
|
||||
let _: f32 = &0.0 + &0.0;
|
||||
|
||||
let _: u8 = 0 << 0;
|
||||
let _: u8 = 0 << &0;
|
||||
let _: u8 = &0 << 0;
|
||||
let _: u8 = &0 << &0;
|
||||
|
||||
// Test type inference when variable types are indirectly inferred.
|
||||
let a = 22;
|
||||
let _: usize = a + &44;
|
||||
|
||||
// When we have no expected type, the types of the operands is the default type.
|
||||
let _ = 0 + 0;
|
||||
let _ = 0 + &0;
|
||||
let _ = &0 + 0;
|
||||
let _ = &0 + &0;
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
enum Blah { A(isize, isize, usize), B(isize, isize) }
|
||||
|
||||
fn main() { match Blah::A(1, 1, 2) { Blah::A(_, x, y) | Blah::B(x, y) => { } } }
|
||||
//~^ ERROR mismatched types
|
@ -1,9 +0,0 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/or-pattern-mismatch.rs:3:68
|
||||
|
|
||||
LL | fn main() { match Blah::A(1, 1, 2) { Blah::A(_, x, y) | Blah::B(x, y) => { } } }
|
||||
| ---------------- this expression has type `Blah` ^ expected `usize`, found `isize`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
68
src/test/ui/or-patterns/or-patterns-binding-type-mismatch.rs
Normal file
68
src/test/ui/or-patterns/or-patterns-binding-type-mismatch.rs
Normal file
@ -0,0 +1,68 @@
|
||||
// Here we test type checking of bindings when combined with or-patterns.
|
||||
// Specifically, we ensure that introducing bindings of different types result in type errors.
|
||||
|
||||
#![feature(or_patterns)]
|
||||
|
||||
fn main() {
|
||||
enum Blah {
|
||||
A(isize, isize, usize),
|
||||
B(isize, isize),
|
||||
}
|
||||
|
||||
match Blah::A(1, 1, 2) {
|
||||
Blah::A(_, x, y) | Blah::B(x, y) => {} //~ ERROR mismatched types
|
||||
}
|
||||
|
||||
match Some(Blah::A(1, 1, 2)) {
|
||||
Some(Blah::A(_, x, y) | Blah::B(x, y)) => {} //~ ERROR mismatched types
|
||||
}
|
||||
|
||||
match (0u8, 1u16) {
|
||||
(x, y) | (y, x) => {} //~ ERROR mismatched types
|
||||
//~^ ERROR mismatched types
|
||||
}
|
||||
|
||||
match Some((0u8, Some((1u16, 2u32)))) {
|
||||
Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) => {}
|
||||
//~^ ERROR mismatched types
|
||||
//~| ERROR mismatched types
|
||||
//~| ERROR mismatched types
|
||||
//~| ERROR mismatched types
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if let Blah::A(_, x, y) | Blah::B(x, y) = Blah::A(1, 1, 2) {
|
||||
//~^ ERROR mismatched types
|
||||
}
|
||||
|
||||
if let Some(Blah::A(_, x, y) | Blah::B(x, y)) = Some(Blah::A(1, 1, 2)) {
|
||||
//~^ ERROR mismatched types
|
||||
}
|
||||
|
||||
if let (x, y) | (y, x) = (0u8, 1u16) {
|
||||
//~^ ERROR mismatched types
|
||||
//~| ERROR mismatched types
|
||||
}
|
||||
|
||||
if let Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x))))
|
||||
//~^ ERROR mismatched types
|
||||
//~| ERROR mismatched types
|
||||
//~| ERROR mismatched types
|
||||
//~| ERROR mismatched types
|
||||
= Some((0u8, Some((1u16, 2u32))))
|
||||
{}
|
||||
|
||||
let Blah::A(_, x, y) | Blah::B(x, y) = Blah::A(1, 1, 2);
|
||||
//~^ ERROR mismatched types
|
||||
|
||||
let (x, y) | (y, x) = (0u8, 1u16);
|
||||
//~^ ERROR mismatched types
|
||||
//~| ERROR mismatched types
|
||||
|
||||
fn f1((Blah::A(_, x, y) | Blah::B(x, y)): Blah) {}
|
||||
//~^ ERROR mismatched types
|
||||
|
||||
fn f2(((x, y) | (y, x)): (u8, u16)) {}
|
||||
//~^ ERROR mismatched types
|
||||
//~| ERROR mismatched types
|
||||
}
|
183
src/test/ui/or-patterns/or-patterns-binding-type-mismatch.stderr
Normal file
183
src/test/ui/or-patterns/or-patterns-binding-type-mismatch.stderr
Normal file
@ -0,0 +1,183 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/or-patterns-binding-type-mismatch.rs:13:39
|
||||
|
|
||||
LL | match Blah::A(1, 1, 2) {
|
||||
| ---------------- this expression has type `main::Blah`
|
||||
LL | Blah::A(_, x, y) | Blah::B(x, y) => {}
|
||||
| ^ expected `usize`, found `isize`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/or-patterns-binding-type-mismatch.rs:17:44
|
||||
|
|
||||
LL | match Some(Blah::A(1, 1, 2)) {
|
||||
| ---------------------- this expression has type `std::option::Option<main::Blah>`
|
||||
LL | Some(Blah::A(_, x, y) | Blah::B(x, y)) => {}
|
||||
| ^ expected `usize`, found `isize`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/or-patterns-binding-type-mismatch.rs:21:19
|
||||
|
|
||||
LL | match (0u8, 1u16) {
|
||||
| ----------- this expression has type `(u8, u16)`
|
||||
LL | (x, y) | (y, x) => {}
|
||||
| ^ expected `u16`, found `u8`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/or-patterns-binding-type-mismatch.rs:21:22
|
||||
|
|
||||
LL | match (0u8, 1u16) {
|
||||
| ----------- this expression has type `(u8, u16)`
|
||||
LL | (x, y) | (y, x) => {}
|
||||
| ^ expected `u8`, found `u16`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/or-patterns-binding-type-mismatch.rs:26:41
|
||||
|
|
||||
LL | match Some((0u8, Some((1u16, 2u32)))) {
|
||||
| ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>`
|
||||
LL | Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) => {}
|
||||
| ^ expected `u16`, found `u8`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/or-patterns-binding-type-mismatch.rs:26:50
|
||||
|
|
||||
LL | match Some((0u8, Some((1u16, 2u32)))) {
|
||||
| ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>`
|
||||
LL | Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) => {}
|
||||
| ^ expected `u8`, found `u16`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/or-patterns-binding-type-mismatch.rs:26:59
|
||||
|
|
||||
LL | match Some((0u8, Some((1u16, 2u32)))) {
|
||||
| ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>`
|
||||
LL | Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) => {}
|
||||
| ^ expected `u32`, found `u16`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/or-patterns-binding-type-mismatch.rs:26:62
|
||||
|
|
||||
LL | match Some((0u8, Some((1u16, 2u32)))) {
|
||||
| ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>`
|
||||
LL | Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) => {}
|
||||
| ^ expected `u8`, found `u32`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/or-patterns-binding-type-mismatch.rs:34:42
|
||||
|
|
||||
LL | if let Blah::A(_, x, y) | Blah::B(x, y) = Blah::A(1, 1, 2) {
|
||||
| ^ ---------------- this expression has type `main::Blah`
|
||||
| |
|
||||
| expected `usize`, found `isize`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/or-patterns-binding-type-mismatch.rs:38:47
|
||||
|
|
||||
LL | if let Some(Blah::A(_, x, y) | Blah::B(x, y)) = Some(Blah::A(1, 1, 2)) {
|
||||
| ^ ---------------------- this expression has type `std::option::Option<main::Blah>`
|
||||
| |
|
||||
| expected `usize`, found `isize`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/or-patterns-binding-type-mismatch.rs:42:22
|
||||
|
|
||||
LL | if let (x, y) | (y, x) = (0u8, 1u16) {
|
||||
| ^ ----------- this expression has type `(u8, u16)`
|
||||
| |
|
||||
| expected `u16`, found `u8`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/or-patterns-binding-type-mismatch.rs:42:25
|
||||
|
|
||||
LL | if let (x, y) | (y, x) = (0u8, 1u16) {
|
||||
| ^ ----------- this expression has type `(u8, u16)`
|
||||
| |
|
||||
| expected `u8`, found `u16`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/or-patterns-binding-type-mismatch.rs:47:44
|
||||
|
|
||||
LL | if let Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x))))
|
||||
| ^ expected `u16`, found `u8`
|
||||
...
|
||||
LL | = Some((0u8, Some((1u16, 2u32))))
|
||||
| ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/or-patterns-binding-type-mismatch.rs:47:53
|
||||
|
|
||||
LL | if let Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x))))
|
||||
| ^ expected `u8`, found `u16`
|
||||
...
|
||||
LL | = Some((0u8, Some((1u16, 2u32))))
|
||||
| ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/or-patterns-binding-type-mismatch.rs:47:62
|
||||
|
|
||||
LL | if let Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x))))
|
||||
| ^ expected `u32`, found `u16`
|
||||
...
|
||||
LL | = Some((0u8, Some((1u16, 2u32))))
|
||||
| ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/or-patterns-binding-type-mismatch.rs:47:65
|
||||
|
|
||||
LL | if let Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x))))
|
||||
| ^ expected `u8`, found `u32`
|
||||
...
|
||||
LL | = Some((0u8, Some((1u16, 2u32))))
|
||||
| ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/or-patterns-binding-type-mismatch.rs:55:39
|
||||
|
|
||||
LL | let Blah::A(_, x, y) | Blah::B(x, y) = Blah::A(1, 1, 2);
|
||||
| ^ ---------------- this expression has type `main::Blah`
|
||||
| |
|
||||
| expected `usize`, found `isize`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/or-patterns-binding-type-mismatch.rs:58:19
|
||||
|
|
||||
LL | let (x, y) | (y, x) = (0u8, 1u16);
|
||||
| ^ ----------- this expression has type `(u8, u16)`
|
||||
| |
|
||||
| expected `u16`, found `u8`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/or-patterns-binding-type-mismatch.rs:58:22
|
||||
|
|
||||
LL | let (x, y) | (y, x) = (0u8, 1u16);
|
||||
| ^ ----------- this expression has type `(u8, u16)`
|
||||
| |
|
||||
| expected `u8`, found `u16`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/or-patterns-binding-type-mismatch.rs:62:42
|
||||
|
|
||||
LL | fn f1((Blah::A(_, x, y) | Blah::B(x, y)): Blah) {}
|
||||
| ^ ---- expected due to this
|
||||
| |
|
||||
| expected `usize`, found `isize`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/or-patterns-binding-type-mismatch.rs:65:22
|
||||
|
|
||||
LL | fn f2(((x, y) | (y, x)): (u8, u16)) {}
|
||||
| ^ --------- expected due to this
|
||||
| |
|
||||
| expected `u16`, found `u8`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/or-patterns-binding-type-mismatch.rs:65:25
|
||||
|
|
||||
LL | fn f2(((x, y) | (y, x)): (u8, u16)) {}
|
||||
| ^ --------- expected due to this
|
||||
| |
|
||||
| expected `u8`, found `u16`
|
||||
|
||||
error: aborting due to 22 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
132
src/test/ui/or-patterns/or-patterns-default-binding-modes.rs
Normal file
132
src/test/ui/or-patterns/or-patterns-default-binding-modes.rs
Normal file
@ -0,0 +1,132 @@
|
||||
// Test that or-patterns are pass-through with respect to default binding modes.
|
||||
|
||||
// check-pass
|
||||
|
||||
#![feature(or_patterns)]
|
||||
#![allow(irrefutable_let_patterns)]
|
||||
|
||||
fn main() {
|
||||
// A regression test for a mistake we made at one point:
|
||||
match &1 {
|
||||
e @ &(1..=2) | e @ &(3..=4) => {}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match &0 {
|
||||
0 | &1 => {}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
type R<'a> = &'a Result<u8, u8>;
|
||||
|
||||
let res: R<'_> = &Ok(0);
|
||||
|
||||
match res {
|
||||
// Alternatives propagate expected type / binding mode independently.
|
||||
Ok(mut x) | &Err(mut x) => drop::<u8>(x),
|
||||
}
|
||||
match res {
|
||||
&(Ok(x) | Err(x)) => drop::<u8>(x),
|
||||
}
|
||||
match res {
|
||||
Ok(x) | Err(x) => drop::<&u8>(x),
|
||||
}
|
||||
if let Ok(mut x) | &Err(mut x) = res {
|
||||
drop::<u8>(x);
|
||||
}
|
||||
if let &(Ok(x) | Err(x)) = res {
|
||||
drop::<u8>(x);
|
||||
}
|
||||
let Ok(mut x) | &Err(mut x) = res;
|
||||
drop::<u8>(x);
|
||||
let &(Ok(x) | Err(x)) = res;
|
||||
drop::<u8>(x);
|
||||
let Ok(x) | Err(x) = res;
|
||||
drop::<&u8>(x);
|
||||
for Ok(mut x) | &Err(mut x) in std::iter::once(res) {
|
||||
drop::<u8>(x);
|
||||
}
|
||||
for &(Ok(x) | Err(x)) in std::iter::once(res) {
|
||||
drop::<u8>(x);
|
||||
}
|
||||
for Ok(x) | Err(x) in std::iter::once(res) {
|
||||
drop::<&u8>(x);
|
||||
}
|
||||
fn f1((Ok(mut x) | &Err(mut x)): R<'_>) {
|
||||
drop::<u8>(x);
|
||||
}
|
||||
fn f2(&(Ok(x) | Err(x)): R<'_>) {
|
||||
drop::<u8>(x);
|
||||
}
|
||||
fn f3((Ok(x) | Err(x)): R<'_>) {
|
||||
drop::<&u8>(x);
|
||||
}
|
||||
|
||||
// Wrap inside another type (a product for a simplity with irrefutable contexts).
|
||||
#[derive(Copy, Clone)]
|
||||
struct Wrap<T>(T);
|
||||
let wres = Wrap(res);
|
||||
|
||||
match wres {
|
||||
Wrap(Ok(mut x) | &Err(mut x)) => drop::<u8>(x),
|
||||
}
|
||||
match wres {
|
||||
Wrap(&(Ok(x) | Err(x))) => drop::<u8>(x),
|
||||
}
|
||||
match wres {
|
||||
Wrap(Ok(x) | Err(x)) => drop::<&u8>(x),
|
||||
}
|
||||
if let Wrap(Ok(mut x) | &Err(mut x)) = wres {
|
||||
drop::<u8>(x);
|
||||
}
|
||||
if let Wrap(&(Ok(x) | Err(x))) = wres {
|
||||
drop::<u8>(x);
|
||||
}
|
||||
if let Wrap(Ok(x) | Err(x)) = wres {
|
||||
drop::<&u8>(x);
|
||||
}
|
||||
let Wrap(Ok(mut x) | &Err(mut x)) = wres;
|
||||
drop::<u8>(x);
|
||||
let Wrap(&(Ok(x) | Err(x))) = wres;
|
||||
drop::<u8>(x);
|
||||
let Wrap(Ok(x) | Err(x)) = wres;
|
||||
drop::<&u8>(x);
|
||||
for Wrap(Ok(mut x) | &Err(mut x)) in std::iter::once(wres) {
|
||||
drop::<u8>(x);
|
||||
}
|
||||
for Wrap(&(Ok(x) | Err(x))) in std::iter::once(wres) {
|
||||
drop::<u8>(x);
|
||||
}
|
||||
for Wrap(Ok(x) | Err(x)) in std::iter::once(wres) {
|
||||
drop::<&u8>(x);
|
||||
}
|
||||
fn fw1(Wrap(Ok(mut x) | &Err(mut x)): Wrap<R<'_>>) {
|
||||
drop::<u8>(x);
|
||||
}
|
||||
fn fw2(Wrap(&(Ok(x) | Err(x))): Wrap<R<'_>>) {
|
||||
drop::<u8>(x);
|
||||
}
|
||||
fn fw3(Wrap(Ok(x) | Err(x)): Wrap<R<'_>>) {
|
||||
drop::<&u8>(x);
|
||||
}
|
||||
|
||||
// Nest some more:
|
||||
|
||||
enum Tri<P> {
|
||||
A(P),
|
||||
B(P),
|
||||
C(P),
|
||||
}
|
||||
|
||||
let tri = &Tri::A(&Ok(0));
|
||||
let Tri::A(Ok(mut x) | Err(mut x))
|
||||
| Tri::B(&Ok(mut x) | Err(mut x))
|
||||
| &Tri::C(Ok(mut x) | Err(mut x)) = tri;
|
||||
drop::<u8>(x);
|
||||
|
||||
match tri {
|
||||
Tri::A(Ok(mut x) | Err(mut x))
|
||||
| Tri::B(&Ok(mut x) | Err(mut x))
|
||||
| &Tri::C(Ok(mut x) | Err(mut x)) => drop::<u8>(x),
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user