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:
bors 2020-02-14 23:11:03 +00:00
commit b92c6ee882
20 changed files with 728 additions and 220 deletions

View File

@ -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"

View File

@ -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)
}
}

View File

@ -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,

View File

@ -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())
}

View File

@ -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;

View File

@ -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
}
}

View File

@ -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,

View File

@ -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();

View File

@ -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,

View File

@ -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.
//

View File

@ -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"] }

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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`.

View 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
}

View 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`.

View 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),
}
}