From 3b37d941a29d91afbfa06afcb63f702ee7b810d6 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 17 Oct 2020 19:01:10 +0100 Subject: [PATCH 01/33] Add some tests --- .../const_in_pattern}/issue-44333.rs | 0 .../const_in_pattern}/issue-44333.stderr | 0 .../ui/consts/const_in_pattern/issue-78057.rs | 17 ++ .../const_in_pattern/issue-78057.stderr | 20 +++ .../ui/pattern/usefulness/consts-opaque.rs | 113 +++++++++++++ .../pattern/usefulness/consts-opaque.stderr | 154 ++++++++++++++++++ 6 files changed, 304 insertions(+) rename src/test/ui/{issues => consts/const_in_pattern}/issue-44333.rs (100%) rename src/test/ui/{issues => consts/const_in_pattern}/issue-44333.stderr (100%) create mode 100644 src/test/ui/consts/const_in_pattern/issue-78057.rs create mode 100644 src/test/ui/consts/const_in_pattern/issue-78057.stderr create mode 100644 src/test/ui/pattern/usefulness/consts-opaque.rs create mode 100644 src/test/ui/pattern/usefulness/consts-opaque.stderr diff --git a/src/test/ui/issues/issue-44333.rs b/src/test/ui/consts/const_in_pattern/issue-44333.rs similarity index 100% rename from src/test/ui/issues/issue-44333.rs rename to src/test/ui/consts/const_in_pattern/issue-44333.rs diff --git a/src/test/ui/issues/issue-44333.stderr b/src/test/ui/consts/const_in_pattern/issue-44333.stderr similarity index 100% rename from src/test/ui/issues/issue-44333.stderr rename to src/test/ui/consts/const_in_pattern/issue-44333.stderr diff --git a/src/test/ui/consts/const_in_pattern/issue-78057.rs b/src/test/ui/consts/const_in_pattern/issue-78057.rs new file mode 100644 index 0000000000000..69cf8404da18e --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/issue-78057.rs @@ -0,0 +1,17 @@ +#![deny(unreachable_patterns)] + +#[derive(PartialEq)] +struct Opaque(i32); + +impl Eq for Opaque {} + +const FOO: Opaque = Opaque(42); + +fn main() { + match FOO { + FOO => {}, + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + _ => {} + //~^ ERROR unreachable pattern + } +} diff --git a/src/test/ui/consts/const_in_pattern/issue-78057.stderr b/src/test/ui/consts/const_in_pattern/issue-78057.stderr new file mode 100644 index 0000000000000..0d49d0e96c854 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/issue-78057.stderr @@ -0,0 +1,20 @@ +error: to use a constant of type `Opaque` in a pattern, `Opaque` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/issue-78057.rs:12:9 + | +LL | FOO => {}, + | ^^^ + +error: unreachable pattern + --> $DIR/issue-78057.rs:14:9 + | +LL | _ => {} + | ^ + | +note: the lint level is defined here + --> $DIR/issue-78057.rs:1:9 + | +LL | #![deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/pattern/usefulness/consts-opaque.rs b/src/test/ui/pattern/usefulness/consts-opaque.rs new file mode 100644 index 0000000000000..928756723a6c9 --- /dev/null +++ b/src/test/ui/pattern/usefulness/consts-opaque.rs @@ -0,0 +1,113 @@ +// This file tests the exhaustiveness algorithm on opaque constants. Most of the examples give +// unnecessary warnings because const_to_pat.rs converts a constant pattern to a wildcard when the +// constant is not allowed as a pattern. This is an edge case so we may not care to fix it. +// See also https://github.com/rust-lang/rust/issues/78057 + +#![deny(unreachable_patterns)] + +#[derive(PartialEq)] +struct Foo(i32); +impl Eq for Foo {} +const FOO: Foo = Foo(42); +const FOO_REF: &Foo = &Foo(42); +const FOO_REF_REF: &&Foo = &&Foo(42); + +#[derive(PartialEq)] +struct Bar; +impl Eq for Bar {} +const BAR: Bar = Bar; + +#[derive(PartialEq)] +enum Baz { + Baz1, + Baz2 +} +impl Eq for Baz {} +const BAZ: Baz = Baz::Baz1; + +type Quux = fn(usize, usize) -> usize; +fn quux(a: usize, b: usize) -> usize { a + b } +const QUUX: Quux = quux; + +fn main() { + match FOO { + FOO => {} + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + _ => {} // should not be emitting unreachable warning + //~^ ERROR unreachable pattern + } + + match FOO_REF { + FOO_REF => {} + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + Foo(_) => {} // should not be emitting unreachable warning + //~^ ERROR unreachable pattern + } + + // FIXME: this causes an ICE (https://github.com/rust-lang/rust/issues/78071) + //match FOO_REF_REF { + // FOO_REF_REF => {} + // Foo(_) => {} + //} + + match BAR { + Bar => {} + BAR => {} // should not be emitting unreachable warning + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR unreachable pattern + _ => {} + //~^ ERROR unreachable pattern + } + + match BAR { + BAR => {} + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + Bar => {} // should not be emitting unreachable warning + //~^ ERROR unreachable pattern + _ => {} + //~^ ERROR unreachable pattern + } + + match BAR { + BAR => {} + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + BAR => {} // should not be emitting unreachable warning + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR unreachable pattern + _ => {} // should not be emitting unreachable warning + //~^ ERROR unreachable pattern + } + + match BAZ { + BAZ => {} + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + Baz::Baz1 => {} // should not be emitting unreachable warning + //~^ ERROR unreachable pattern + _ => {} + //~^ ERROR unreachable pattern + } + + match BAZ { + Baz::Baz1 => {} + BAZ => {} + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + _ => {} + //~^ ERROR unreachable pattern + } + + match BAZ { + BAZ => {} + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + Baz::Baz2 => {} // should not be emitting unreachable warning + //~^ ERROR unreachable pattern + _ => {} // should not be emitting unreachable warning + //~^ ERROR unreachable pattern + } + + match QUUX { + QUUX => {} + QUUX => {} // should not be emitting unreachable warning + //~^ ERROR unreachable pattern + _ => {} + } +} diff --git a/src/test/ui/pattern/usefulness/consts-opaque.stderr b/src/test/ui/pattern/usefulness/consts-opaque.stderr new file mode 100644 index 0000000000000..07cdc1c95fad0 --- /dev/null +++ b/src/test/ui/pattern/usefulness/consts-opaque.stderr @@ -0,0 +1,154 @@ +error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:34:9 + | +LL | FOO => {} + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:36:9 + | +LL | _ => {} // should not be emitting unreachable warning + | ^ + | +note: the lint level is defined here + --> $DIR/consts-opaque.rs:6:9 + | +LL | #![deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:41:9 + | +LL | FOO_REF => {} + | ^^^^^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:43:9 + | +LL | Foo(_) => {} // should not be emitting unreachable warning + | ^^^^^^ + +error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:55:9 + | +LL | BAR => {} // should not be emitting unreachable warning + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:55:9 + | +LL | Bar => {} + | --- matches any value +LL | BAR => {} // should not be emitting unreachable warning + | ^^^ unreachable pattern + +error: unreachable pattern + --> $DIR/consts-opaque.rs:58:9 + | +LL | Bar => {} + | --- matches any value +... +LL | _ => {} + | ^ unreachable pattern + +error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:63:9 + | +LL | BAR => {} + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:65:9 + | +LL | Bar => {} // should not be emitting unreachable warning + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:67:9 + | +LL | Bar => {} // should not be emitting unreachable warning + | --- matches any value +LL | +LL | _ => {} + | ^ unreachable pattern + +error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:72:9 + | +LL | BAR => {} + | ^^^ + +error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:74:9 + | +LL | BAR => {} // should not be emitting unreachable warning + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:74:9 + | +LL | BAR => {} // should not be emitting unreachable warning + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:77:9 + | +LL | _ => {} // should not be emitting unreachable warning + | ^ + +error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:82:9 + | +LL | BAZ => {} + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:84:9 + | +LL | Baz::Baz1 => {} // should not be emitting unreachable warning + | ^^^^^^^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:86:9 + | +LL | _ => {} + | ^ + +error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:92:9 + | +LL | BAZ => {} + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:94:9 + | +LL | _ => {} + | ^ + +error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:99:9 + | +LL | BAZ => {} + | ^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:101:9 + | +LL | Baz::Baz2 => {} // should not be emitting unreachable warning + | ^^^^^^^^^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:103:9 + | +LL | _ => {} // should not be emitting unreachable warning + | ^ + +error: unreachable pattern + --> $DIR/consts-opaque.rs:109:9 + | +LL | QUUX => {} // should not be emitting unreachable warning + | ^^^^ + +error: aborting due to 23 previous errors + From bb8111069ed38455d915f7f8f0e050bb3c3720cf Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Tue, 29 Sep 2020 18:28:39 +0200 Subject: [PATCH 02/33] Destructure byte array constants to array patterns instead of keeping them opaque --- .../src/thir/pattern/_match.rs | 32 +++---------------- .../src/thir/pattern/const_to_pat.rs | 11 ++++--- .../match-byte-array-patterns-2.stderr | 4 +-- 3 files changed, 13 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index 904524e13ae08..b657f25f1edb1 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -304,12 +304,11 @@ use std::iter::{FromIterator, IntoIterator}; use std::ops::RangeInclusive; crate fn expand_pattern<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: Pat<'tcx>) -> Pat<'tcx> { - LiteralExpander { tcx: cx.tcx, param_env: cx.param_env }.fold_pattern(&pat) + LiteralExpander { tcx: cx.tcx }.fold_pattern(&pat) } struct LiteralExpander<'tcx> { tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, } impl<'tcx> LiteralExpander<'tcx> { @@ -328,40 +327,17 @@ impl<'tcx> LiteralExpander<'tcx> { ) -> ConstValue<'tcx> { debug!("fold_const_value_deref {:?} {:?} {:?}", val, rty, crty); match (val, &crty.kind(), &rty.kind()) { - // the easy case, deref a reference - (ConstValue::Scalar(p), x, y) if x == y => { - match p { - Scalar::Ptr(p) => { - let alloc = self.tcx.global_alloc(p.alloc_id).unwrap_memory(); - ConstValue::ByRef { alloc, offset: p.offset } - } - Scalar::Raw { .. } => { - let layout = self.tcx.layout_of(self.param_env.and(rty)).unwrap(); - if layout.is_zst() { - // Deref of a reference to a ZST is a nop. - ConstValue::Scalar(Scalar::zst()) - } else { - // FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;` - bug!("cannot deref {:#?}, {} -> {}", val, crty, rty); - } - } - } - } // unsize array to slice if pattern is array but match value or other patterns are slice (ConstValue::Scalar(Scalar::Ptr(p)), ty::Array(t, n), ty::Slice(u)) => { assert_eq!(t, u); + assert_eq!(p.offset, Size::ZERO); ConstValue::Slice { data: self.tcx.global_alloc(p.alloc_id).unwrap_memory(), - start: p.offset.bytes().try_into().unwrap(), + start: 0, end: n.eval_usize(self.tcx, ty::ParamEnv::empty()).try_into().unwrap(), } } - // fat pointers stay the same - (ConstValue::Slice { .. }, _, _) - | (_, ty::Slice(_), ty::Slice(_)) - | (_, ty::Str, ty::Str) => val, - // FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;` being used - _ => bug!("cannot deref {:#?}, {} -> {}", val, crty, rty), + _ => val, } } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index a203b3a142863..32b9b0a2d3b14 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -390,11 +390,14 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { ty::Slice(elem_ty) if elem_ty == tcx.types.u8 => PatKind::Constant { value: cv }, // `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when // matching against references, you can only use byte string literals. - // FIXME: clean this up, likely by permitting array patterns when matching on slices - ty::Array(elem_ty, _) if elem_ty == tcx.types.u8 => PatKind::Constant { value: cv }, + // The typechecker has a special case for byte string literals, by treating them + // as slices. This means we turn `&[T; N]` constants into slice patterns, which + // has no negative effects on pattern matching, even if we're actually matching on + // arrays. + ty::Array(..) | // Cannot merge this with the catch all branch below, because the `const_deref` - // changes the type from slice to array, and slice patterns behave differently from - // array patterns. + // changes the type from slice to array, we need to keep the original type in the + // pattern. ty::Slice(..) => { let old = self.behind_reference.replace(true); let array = tcx.deref_const(self.param_env.and(cv)); diff --git a/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr b/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr index ffc8433403fd5..7968f9713ff22 100644 --- a/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr +++ b/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr @@ -7,11 +7,11 @@ LL | match buf { = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = note: the matched value is of type `&[u8; 4]` -error[E0004]: non-exhaustive patterns: `&[]`, `&[_]`, `&[_, _]` and 2 more not covered +error[E0004]: non-exhaustive patterns: `&[0_u8..=64_u8, _, _, _]` and `&[66_u8..=u8::MAX, _, _, _]` not covered --> $DIR/match-byte-array-patterns-2.rs:10:11 | LL | match buf { - | ^^^ patterns `&[]`, `&[_]`, `&[_, _]` and 2 more not covered + | ^^^ patterns `&[0_u8..=64_u8, _, _, _]` and `&[66_u8..=u8::MAX, _, _, _]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = note: the matched value is of type `&[u8]` From c3d0445021ce6b4ee9dc96c6a4dab611402988d6 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Thu, 1 Oct 2020 09:24:44 +0200 Subject: [PATCH 03/33] Destructure byte slices and remove all the workarounds --- .../src/thir/pattern/_match.rs | 249 +----------------- .../src/thir/pattern/check_match.rs | 2 +- .../src/thir/pattern/const_to_pat.rs | 1 - 3 files changed, 9 insertions(+), 243 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index b657f25f1edb1..9ca711722a307 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -284,10 +284,9 @@ use super::{FieldPat, Pat, PatKind, PatRange}; use rustc_arena::TypedArena; use rustc_attr::{SignedInt, UnsignedInt}; -use rustc_errors::ErrorReported; use rustc_hir::def_id::DefId; use rustc_hir::{HirId, RangeEnd}; -use rustc_middle::mir::interpret::{truncate, AllocId, ConstValue, Pointer, Scalar}; +use rustc_middle::mir::interpret::{truncate, ConstValue}; use rustc_middle::mir::Field; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{self, Const, Ty, TyCtxt}; @@ -296,84 +295,21 @@ use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{Integer, Size, VariantIdx}; use smallvec::{smallvec, SmallVec}; -use std::borrow::Cow; use std::cmp::{self, max, min, Ordering}; -use std::convert::TryInto; use std::fmt; use std::iter::{FromIterator, IntoIterator}; use std::ops::RangeInclusive; -crate fn expand_pattern<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: Pat<'tcx>) -> Pat<'tcx> { - LiteralExpander { tcx: cx.tcx }.fold_pattern(&pat) +crate fn expand_pattern<'tcx>(pat: Pat<'tcx>) -> Pat<'tcx> { + LiteralExpander.fold_pattern(&pat) } -struct LiteralExpander<'tcx> { - tcx: TyCtxt<'tcx>, -} +struct LiteralExpander; -impl<'tcx> LiteralExpander<'tcx> { - /// Derefs `val` and potentially unsizes the value if `crty` is an array and `rty` a slice. - /// - /// `crty` and `rty` can differ because you can use array constants in the presence of slice - /// patterns. So the pattern may end up being a slice, but the constant is an array. We convert - /// the array to a slice in that case. - fn fold_const_value_deref( - &mut self, - val: ConstValue<'tcx>, - // the pattern's pointee type - rty: Ty<'tcx>, - // the constant's pointee type - crty: Ty<'tcx>, - ) -> ConstValue<'tcx> { - debug!("fold_const_value_deref {:?} {:?} {:?}", val, rty, crty); - match (val, &crty.kind(), &rty.kind()) { - // unsize array to slice if pattern is array but match value or other patterns are slice - (ConstValue::Scalar(Scalar::Ptr(p)), ty::Array(t, n), ty::Slice(u)) => { - assert_eq!(t, u); - assert_eq!(p.offset, Size::ZERO); - ConstValue::Slice { - data: self.tcx.global_alloc(p.alloc_id).unwrap_memory(), - start: 0, - end: n.eval_usize(self.tcx, ty::ParamEnv::empty()).try_into().unwrap(), - } - } - _ => val, - } - } -} - -impl<'tcx> PatternFolder<'tcx> for LiteralExpander<'tcx> { +impl<'tcx> PatternFolder<'tcx> for LiteralExpander { fn fold_pattern(&mut self, pat: &Pat<'tcx>) -> Pat<'tcx> { debug!("fold_pattern {:?} {:?} {:?}", pat, pat.ty.kind(), pat.kind); match (pat.ty.kind(), &*pat.kind) { - (&ty::Ref(_, rty, _), &PatKind::Constant { value: Const { val, ty: const_ty } }) - if const_ty.is_ref() => - { - let crty = - if let ty::Ref(_, crty, _) = const_ty.kind() { crty } else { unreachable!() }; - if let ty::ConstKind::Value(val) = val { - Pat { - ty: pat.ty, - span: pat.span, - kind: box PatKind::Deref { - subpattern: Pat { - ty: rty, - span: pat.span, - kind: box PatKind::Constant { - value: Const::from_value( - self.tcx, - self.fold_const_value_deref(*val, rty, crty), - rty, - ), - }, - }, - }, - } - } else { - bug!("cannot deref {:#?}, {} -> {}", val, crty, rty) - } - } - (_, &PatKind::Binding { subpattern: Some(ref s), .. }) => s.fold_with(self), (_, &PatKind::AscribeUserType { subpattern: ref s, .. }) => s.fold_with(self), _ => pat.super_fold_with(self), @@ -941,8 +877,6 @@ impl<'tcx> Constructor<'tcx> { .iter() .filter_map(|c: &Constructor<'_>| match c { Slice(slice) => Some(*slice), - // FIXME(oli-obk): implement `deref` for `ConstValue` - ConstantValue(..) => None, _ => bug!("bad slice pattern constructor {:?}", c), }) .map(Slice::value_kind); @@ -1162,12 +1096,6 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { Fields::Slice(std::slice::from_ref(pat)) } - /// Construct a new `Fields` from the given patterns. You must be sure those patterns can't - /// contain fields that need to be filtered out. When in doubt, prefer `replace_fields`. - fn from_slice_unfiltered(pats: &'p [Pat<'tcx>]) -> Self { - Fields::Slice(pats) - } - /// Convenience; internal use. fn wildcards_from_tys( cx: &MatchCheckCtxt<'p, 'tcx>, @@ -2183,19 +2111,7 @@ fn pat_constructor<'tcx>( if let Some(int_range) = IntRange::from_const(tcx, param_env, value, pat.span) { Some(IntRange(int_range)) } else { - match (value.val, &value.ty.kind()) { - (_, ty::Array(_, n)) => { - let len = n.eval_usize(tcx, param_env); - Some(Slice(Slice { array_len: Some(len), kind: FixedLen(len) })) - } - (ty::ConstKind::Value(ConstValue::Slice { start, end, .. }), ty::Slice(_)) => { - let len = (end - start) as u64; - Some(Slice(Slice { array_len: None, kind: FixedLen(len) })) - } - // FIXME(oli-obk): implement `deref` for `ConstValue` - // (ty::ConstKind::Value(ConstValue::ByRef { .. }), ty::Slice(_)) => { ... } - _ => Some(ConstantValue(value)), - } + Some(ConstantValue(value)) } } PatKind::Range(PatRange { lo, hi, end }) => { @@ -2230,75 +2146,6 @@ fn pat_constructor<'tcx>( } } -// checks whether a constant is equal to a user-written slice pattern. Only supports byte slices, -// meaning all other types will compare unequal and thus equal patterns often do not cause the -// second pattern to lint about unreachable match arms. -fn slice_pat_covered_by_const<'tcx>( - tcx: TyCtxt<'tcx>, - _span: Span, - const_val: &'tcx ty::Const<'tcx>, - prefix: &[Pat<'tcx>], - slice: &Option>, - suffix: &[Pat<'tcx>], - param_env: ty::ParamEnv<'tcx>, -) -> Result { - let const_val_val = if let ty::ConstKind::Value(val) = const_val.val { - val - } else { - bug!( - "slice_pat_covered_by_const: {:#?}, {:#?}, {:#?}, {:#?}", - const_val, - prefix, - slice, - suffix, - ) - }; - - let data: &[u8] = match (const_val_val, &const_val.ty.kind()) { - (ConstValue::ByRef { offset, alloc, .. }, ty::Array(t, n)) => { - assert_eq!(*t, tcx.types.u8); - let n = n.eval_usize(tcx, param_env); - let ptr = Pointer::new(AllocId(0), offset); - alloc.get_bytes(&tcx, ptr, Size::from_bytes(n)).unwrap() - } - (ConstValue::Slice { data, start, end }, ty::Slice(t)) => { - assert_eq!(*t, tcx.types.u8); - let ptr = Pointer::new(AllocId(0), Size::from_bytes(start)); - data.get_bytes(&tcx, ptr, Size::from_bytes(end - start)).unwrap() - } - // FIXME(oli-obk): create a way to extract fat pointers from ByRef - (_, ty::Slice(_)) => return Ok(false), - _ => bug!( - "slice_pat_covered_by_const: {:#?}, {:#?}, {:#?}, {:#?}", - const_val, - prefix, - slice, - suffix, - ), - }; - - let pat_len = prefix.len() + suffix.len(); - if data.len() < pat_len || (slice.is_none() && data.len() > pat_len) { - return Ok(false); - } - - for (ch, pat) in data[..prefix.len()] - .iter() - .zip(prefix) - .chain(data[data.len() - suffix.len()..].iter().zip(suffix)) - { - if let box PatKind::Constant { value } = pat.kind { - let b = value.eval_bits(tcx, param_env, pat.ty); - assert_eq!(b as u8 as u128, b); - if b as u8 != *ch { - return Ok(false); - } - } - } - - Ok(true) -} - /// For exhaustive integer matching, some constructors are grouped within other constructors /// (namely integer typed values are grouped within ranges). However, when specialising these /// constructors, we want to be specialising for the underlying constructors (the integers), not @@ -2670,73 +2517,7 @@ fn specialize_one_pattern<'p, 'tcx>( PatKind::Deref { ref subpattern } => Some(Fields::from_single_pattern(subpattern)), PatKind::Constant { value } if constructor.is_slice() => { - // We extract an `Option` for the pointer because slices of zero - // elements don't necessarily point to memory, they are usually - // just integers. The only time they should be pointing to memory - // is when they are subslices of nonzero slices. - let (alloc, offset, n, ty) = match value.ty.kind() { - ty::Array(t, n) => { - let n = n.eval_usize(cx.tcx, cx.param_env); - // Shortcut for `n == 0` where no matter what `alloc` and `offset` we produce, - // the result would be exactly what we early return here. - if n == 0 { - if ctor_wild_subpatterns.len() as u64 != n { - return None; - } - return Some(Fields::empty()); - } - match value.val { - ty::ConstKind::Value(ConstValue::ByRef { offset, alloc, .. }) => { - (Cow::Borrowed(alloc), offset, n, t) - } - _ => span_bug!(pat.span, "array pattern is {:?}", value,), - } - } - ty::Slice(t) => { - match value.val { - ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => { - let offset = Size::from_bytes(start); - let n = (end - start) as u64; - (Cow::Borrowed(data), offset, n, t) - } - ty::ConstKind::Value(ConstValue::ByRef { .. }) => { - // FIXME(oli-obk): implement `deref` for `ConstValue` - return None; - } - _ => span_bug!( - pat.span, - "slice pattern constant must be scalar pair but is {:?}", - value, - ), - } - } - _ => span_bug!( - pat.span, - "unexpected const-val {:?} with ctor {:?}", - value, - constructor, - ), - }; - if ctor_wild_subpatterns.len() as u64 != n { - return None; - } - - // Convert a constant slice/array pattern to a list of patterns. - let layout = cx.tcx.layout_of(cx.param_env.and(ty)).ok()?; - let ptr = Pointer::new(AllocId(0), offset); - let pats = cx.pattern_arena.alloc_from_iter((0..n).filter_map(|i| { - let ptr = ptr.offset(layout.size * i, &cx.tcx).ok()?; - let scalar = alloc.read_scalar(&cx.tcx, ptr, layout.size).ok()?; - let scalar = scalar.check_init().ok()?; - let value = ty::Const::from_scalar(cx.tcx, scalar, ty); - let pattern = Pat { ty, span: pat.span, kind: box PatKind::Constant { value } }; - Some(pattern) - })); - // Ensure none of the dereferences failed. - if pats.len() as u64 != n { - return None; - } - Some(Fields::from_slice_unfiltered(pats)) + span_bug!(pat.span, "unexpected const-val {:?} with ctor {:?}", value, constructor) } PatKind::Constant { .. } | PatKind::Range { .. } => { @@ -2778,21 +2559,7 @@ fn specialize_one_pattern<'p, 'tcx>( let suffix = suffix.iter().enumerate().map(|(i, p)| (arity - suffix.len() + i, p)); Some(ctor_wild_subpatterns.replace_fields_indexed(prefix.chain(suffix))) } - ConstantValue(cv) => { - match slice_pat_covered_by_const( - cx.tcx, - pat.span, - cv, - prefix, - slice, - suffix, - cx.param_env, - ) { - Ok(true) => Some(Fields::empty()), - Ok(false) => None, - Err(ErrorReported) => None, - } - } + ConstantValue(_) => None, _ => span_bug!(pat.span, "unexpected ctor {:?} for slice pat", constructor), }, diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 047bf7db4c867..f623f3f631378 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -140,7 +140,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { patcx.include_lint_checks(); let pattern = patcx.lower_pattern(pat); let pattern_ty = pattern.ty; - let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(cx, pattern)); + let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(pattern)); if !patcx.errors.is_empty() { *have_errors = true; patcx.report_inlining_errors(pat.span); diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 32b9b0a2d3b14..6370f8c375b2a 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -387,7 +387,6 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { // `&str` and `&[u8]` are represented as `ConstValue::Slice`, let's keep using this // optimization for now. ty::Str => PatKind::Constant { value: cv }, - ty::Slice(elem_ty) if elem_ty == tcx.types.u8 => PatKind::Constant { value: cv }, // `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when // matching against references, you can only use byte string literals. // The typechecker has a special case for byte string literals, by treating them From 99852e0db6cdb62894e87eae2a41310b8400a5bf Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 17 Oct 2020 20:07:06 +0100 Subject: [PATCH 04/33] A ConstantValue constructor with a slice pattern is an error --- compiler/rustc_mir_build/src/thir/pattern/_match.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index 9ca711722a307..d16ba1916819c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -2559,7 +2559,6 @@ fn specialize_one_pattern<'p, 'tcx>( let suffix = suffix.iter().enumerate().map(|(i, p)| (arity - suffix.len() + i, p)); Some(ctor_wild_subpatterns.replace_fields_indexed(prefix.chain(suffix))) } - ConstantValue(_) => None, _ => span_bug!(pat.span, "unexpected ctor {:?} for slice pat", constructor), }, From 3708c86de1a77d47f53fd376659184a64f3ce706 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Fri, 2 Oct 2020 14:19:52 +0200 Subject: [PATCH 05/33] Treat booleans as integers with valid range 0..=1 --- compiler/rustc_mir_build/src/thir/pattern/_match.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index d16ba1916819c..ac5e114d45bf4 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -1485,9 +1485,7 @@ fn all_constructors<'a, 'tcx>( ) }; match *pcx.ty.kind() { - ty::Bool => { - [true, false].iter().map(|&b| ConstantValue(ty::Const::from_bool(cx.tcx, b))).collect() - } + ty::Bool => vec![make_range(0, 1)], ty::Array(ref sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => { let len = len.eval_usize(cx.tcx, cx.param_env); if len != 0 && cx.is_uninhabited(sub_ty) { @@ -1600,7 +1598,7 @@ impl<'tcx> IntRange<'tcx> { #[inline] fn is_integral(ty: Ty<'_>) -> bool { match ty.kind() { - ty::Char | ty::Int(_) | ty::Uint(_) => true, + ty::Char | ty::Int(_) | ty::Uint(_) | ty::Bool => true, _ => false, } } @@ -1622,6 +1620,7 @@ impl<'tcx> IntRange<'tcx> { #[inline] fn integral_size_and_signed_bias(tcx: TyCtxt<'tcx>, ty: Ty<'_>) -> Option<(Size, u128)> { match *ty.kind() { + ty::Bool => Some((Size::from_bytes(1), 0)), ty::Char => Some((Size::from_bytes(4), 0)), ty::Int(ity) => { let size = Integer::from_attr(&tcx, SignedInt(ity)).size(); From f504e9a42500db67d3c22b07f90df4d6ebc41758 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 17 Oct 2020 20:11:30 +0100 Subject: [PATCH 06/33] Fix comment --- compiler/rustc_mir_build/src/thir/pattern/_match.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index ac5e114d45bf4..7124cad513ecc 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -619,6 +619,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { /// +++++++++++++++++++++++++++++ /// + _ + [_, _, tail @ ..] + /// +++++++++++++++++++++++++++++ +/// ``` impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "\n")?; From aa4172076ea9b886fefadafda6e479ab6c574bff Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 17 Oct 2020 23:12:28 +0100 Subject: [PATCH 07/33] Handle ranges of float consistently This deconfuses the comparison of floats, that currently mixed ranges and non-ranges. --- .../src/thir/pattern/_match.rs | 105 ++++++++---------- 1 file changed, 48 insertions(+), 57 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index 7124cad513ecc..6a37dd896b6c3 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -834,13 +834,6 @@ enum Constructor<'tcx> { } impl<'tcx> Constructor<'tcx> { - fn is_slice(&self) -> bool { - match self { - Slice(_) => true, - _ => false, - } - } - fn variant_index_for_adt<'a>( &self, cx: &MatchCheckCtxt<'a, 'tcx>, @@ -2111,7 +2104,10 @@ fn pat_constructor<'tcx>( if let Some(int_range) = IntRange::from_const(tcx, param_env, value, pat.span) { Some(IntRange(int_range)) } else { - Some(ConstantValue(value)) + match value.ty.kind() { + ty::Float(_) => Some(FloatRange(value, value, RangeEnd::Included)), + _ => Some(ConstantValue(value)), + } } } PatKind::Range(PatRange { lo, hi, end }) => { @@ -2443,35 +2439,6 @@ fn lint_overlapping_patterns<'tcx>( } } -fn constructor_covered_by_range<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ctor: &Constructor<'tcx>, - pat: &Pat<'tcx>, -) -> Option<()> { - if let Single = ctor { - return Some(()); - } - - let (pat_from, pat_to, pat_end, ty) = match *pat.kind { - PatKind::Constant { value } => (value, value, RangeEnd::Included, value.ty), - PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end, lo.ty), - _ => bug!("`constructor_covered_by_range` called with {:?}", pat), - }; - let (ctor_from, ctor_to, ctor_end) = match *ctor { - ConstantValue(value) => (value, value, RangeEnd::Included), - FloatRange(from, to, ctor_end) => (from, to, ctor_end), - _ => bug!("`constructor_covered_by_range` called with {:?}", ctor), - }; - trace!("constructor_covered_by_range {:#?}, {:#?}, {:#?}, {}", ctor, pat_from, pat_to, ty); - - let to = compare_const_vals(tcx, ctor_to, pat_to, param_env, ty)?; - let from = compare_const_vals(tcx, ctor_from, pat_from, param_env, ty)?; - let intersects = (from == Ordering::Greater || from == Ordering::Equal) - && (to == Ordering::Less || (pat_end == ctor_end && to == Ordering::Equal)); - if intersects { Some(()) } else { None } -} - /// This is the main specialization step. It expands the pattern /// into `arity` patterns based on the constructor. For most patterns, the step is trivial, /// for instance tuple patterns are flattened and box patterns expand into their inner pattern. @@ -2516,27 +2483,51 @@ fn specialize_one_pattern<'p, 'tcx>( PatKind::Deref { ref subpattern } => Some(Fields::from_single_pattern(subpattern)), - PatKind::Constant { value } if constructor.is_slice() => { - span_bug!(pat.span, "unexpected const-val {:?} with ctor {:?}", value, constructor) - } - PatKind::Constant { .. } | PatKind::Range { .. } => { - // If the constructor is a: - // - Single value: add a row if the pattern contains the constructor. - // - Range: add a row if the constructor intersects the pattern. - if let IntRange(ctor) = constructor { - let pat = IntRange::from_pat(cx.tcx, cx.param_env, pat)?; - ctor.intersection(cx.tcx, &pat)?; - // Constructor splitting should ensure that all intersections we encounter - // are actually inclusions. - assert!(ctor.is_subrange(&pat)); - } else { - // Fallback for non-ranges and ranges that involve - // floating-point numbers, which are not conveniently handled - // by `IntRange`. For these cases, the constructor may not be a - // range so intersection actually devolves into being covered - // by the pattern. - constructor_covered_by_range(cx.tcx, cx.param_env, constructor, pat)?; + match constructor { + Single => {} + IntRange(ctor) => { + let pat = IntRange::from_pat(cx.tcx, cx.param_env, pat)?; + ctor.intersection(cx.tcx, &pat)?; + // Constructor splitting should ensure that all intersections we encounter + // are actually inclusions. + assert!(ctor.is_subrange(&pat)); + } + FloatRange(ctor_from, ctor_to, ctor_end) => { + let (pat_from, pat_to, pat_end, ty) = match *pat.kind { + PatKind::Constant { value } => (value, value, RangeEnd::Included, value.ty), + PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end, lo.ty), + _ => unreachable!(), // This is ensured by the branch we're in + }; + let to = compare_const_vals(cx.tcx, ctor_to, pat_to, cx.param_env, ty)?; + let from = compare_const_vals(cx.tcx, ctor_from, pat_from, cx.param_env, ty)?; + let intersects = (from == Ordering::Greater || from == Ordering::Equal) + && (to == Ordering::Less + || (pat_end == *ctor_end && to == Ordering::Equal)); + if !intersects { + return None; + } + } + ConstantValue(ctor_value) => { + let pat_value = match *pat.kind { + PatKind::Constant { value } => value, + _ => span_bug!( + pat.span, + "unexpected range pattern {:?} for constant value ctor", + pat + ), + }; + + // FIXME: there's probably a more direct way of comparing for equality + if compare_const_vals(cx.tcx, ctor_value, pat_value, cx.param_env, pat.ty)? + != Ordering::Equal + { + return None; + } + } + _ => { + span_bug!(pat.span, "unexpected pattern {:?} with ctor {:?}", pat, constructor) + } } Some(Fields::empty()) } From d1a784e7b9c4cfe9ce216eb89c362726ff890ea0 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 17 Oct 2020 23:18:05 +0100 Subject: [PATCH 08/33] Treat string literals separately from other constants --- compiler/rustc_mir_build/src/thir/pattern/_match.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index 6a37dd896b6c3..31ec83a22baf4 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -827,6 +827,8 @@ enum Constructor<'tcx> { IntRange(IntRange<'tcx>), /// Ranges of floating-point literal values (`2.0..=5.2`). FloatRange(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>, RangeEnd), + /// String literals. Strings are not quite the same as `&[u8]` so we treat them separately. + Str(&'tcx ty::Const<'tcx>), /// Array and slice patterns. Slice(Slice), /// Fake extra constructor for enums that aren't allowed to be matched exhaustively. @@ -863,7 +865,7 @@ impl<'tcx> Constructor<'tcx> { match self { // Those constructors can only match themselves. - Single | Variant(_) | ConstantValue(..) | FloatRange(..) => { + Single | Variant(_) | ConstantValue(..) | Str(..) | FloatRange(..) => { if other_ctors.iter().any(|c| c == self) { vec![] } else { vec![self.clone()] } } &Slice(slice) => { @@ -1013,6 +1015,7 @@ impl<'tcx> Constructor<'tcx> { } }, &ConstantValue(value) => PatKind::Constant { value }, + &Str(value) => PatKind::Constant { value }, &FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }), IntRange(range) => return range.to_pat(cx.tcx), NonExhaustive => PatKind::Wild, @@ -1167,7 +1170,9 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { } _ => bug!("bad slice pattern {:?} {:?}", constructor, ty), }, - ConstantValue(..) | FloatRange(..) | IntRange(..) | NonExhaustive => Fields::empty(), + ConstantValue(..) | Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive => { + Fields::empty() + } }; debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret); ret @@ -2106,6 +2111,7 @@ fn pat_constructor<'tcx>( } else { match value.ty.kind() { ty::Float(_) => Some(FloatRange(value, value, RangeEnd::Included)), + ty::Ref(_, t, _) if t.is_str() => Some(Str(value)), _ => Some(ConstantValue(value)), } } @@ -2508,7 +2514,7 @@ fn specialize_one_pattern<'p, 'tcx>( return None; } } - ConstantValue(ctor_value) => { + ConstantValue(ctor_value) | Str(ctor_value) => { let pat_value = match *pat.kind { PatKind::Constant { value } => value, _ => span_bug!( From da0ba2f64574e0ecca65d0d1045af00abe1515fd Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 18 Oct 2020 13:48:54 +0100 Subject: [PATCH 09/33] The only remaining constant patterns are opaque --- .../src/thir/pattern/_match.rs | 69 +++++++++++-------- .../rustc_mir_build/src/thir/pattern/mod.rs | 7 ++ .../ui/pattern/usefulness/consts-opaque.rs | 3 +- .../pattern/usefulness/consts-opaque.stderr | 8 +-- 4 files changed, 50 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index 31ec83a22baf4..fb068f21d8a69 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -394,9 +394,15 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { cx: &mut MatchCheckCtxt<'p, 'tcx>, constructor: &Constructor<'tcx>, ctor_wild_subpatterns: &Fields<'p, 'tcx>, + is_my_head_ctor: bool, ) -> Option> { - let new_fields = - specialize_one_pattern(cx, self.head(), constructor, ctor_wild_subpatterns)?; + let new_fields = specialize_one_pattern( + cx, + self.head(), + constructor, + ctor_wild_subpatterns, + is_my_head_ctor, + )?; Some(new_fields.push_on_patstack(&self.0[1..])) } } @@ -574,6 +580,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { cx, constructor, ctor_wild_subpatterns, + false, ) }) .collect() @@ -599,7 +606,9 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { SpecializationCache::Incompatible => self .patterns .iter() - .filter_map(|r| r.specialize_constructor(cx, constructor, ctor_wild_subpatterns)) + .filter_map(|r| { + r.specialize_constructor(cx, constructor, ctor_wild_subpatterns, false) + }) .collect(), } } @@ -821,8 +830,6 @@ enum Constructor<'tcx> { Single, /// Enum variants. Variant(DefId), - /// Literal values. - ConstantValue(&'tcx ty::Const<'tcx>), /// Ranges of integer literal values (`2`, `2..=5` or `2..5`). IntRange(IntRange<'tcx>), /// Ranges of floating-point literal values (`2.0..=5.2`). @@ -831,27 +838,22 @@ enum Constructor<'tcx> { Str(&'tcx ty::Const<'tcx>), /// Array and slice patterns. Slice(Slice), + /// Constants that must not be matched structurally. They are treated as black + /// boxes for the purposes of exhaustiveness: we must not inspect them, and they + /// don't count towards making a match exhaustive. + Opaque, /// Fake extra constructor for enums that aren't allowed to be matched exhaustively. NonExhaustive, } impl<'tcx> Constructor<'tcx> { - fn variant_index_for_adt<'a>( - &self, - cx: &MatchCheckCtxt<'a, 'tcx>, - adt: &'tcx ty::AdtDef, - ) -> VariantIdx { + fn variant_index_for_adt(&self, adt: &'tcx ty::AdtDef) -> VariantIdx { match *self { Variant(id) => adt.variant_index_with_id(id), Single => { assert!(!adt.is_enum()); VariantIdx::new(0) } - ConstantValue(c) => cx - .tcx - .destructure_const(cx.param_env.and(c)) - .variant - .expect("destructed const of adt without variant id"), _ => bug!("bad constructor {:?} for adt {:?}", self, adt), } } @@ -865,7 +867,7 @@ impl<'tcx> Constructor<'tcx> { match self { // Those constructors can only match themselves. - Single | Variant(_) | ConstantValue(..) | Str(..) | FloatRange(..) => { + Single | Variant(_) | Str(..) | FloatRange(..) => { if other_ctors.iter().any(|c| c == self) { vec![] } else { vec![self.clone()] } } &Slice(slice) => { @@ -936,6 +938,7 @@ impl<'tcx> Constructor<'tcx> { } // This constructor is never covered by anything else NonExhaustive => vec![NonExhaustive], + Opaque => bug!("unexpected opaque ctor {:?} found in all_ctors", self), } } @@ -975,7 +978,7 @@ impl<'tcx> Constructor<'tcx> { PatKind::Variant { adt_def: adt, substs, - variant_index: self.variant_index_for_adt(cx, adt), + variant_index: self.variant_index_for_adt(adt), subpatterns, } } else { @@ -1014,11 +1017,11 @@ impl<'tcx> Constructor<'tcx> { PatKind::Slice { prefix, slice: Some(wild), suffix } } }, - &ConstantValue(value) => PatKind::Constant { value }, &Str(value) => PatKind::Constant { value }, &FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }), IntRange(range) => return range.to_pat(cx.tcx), NonExhaustive => PatKind::Wild, + Opaque => bug!("we should not try to apply an opaque constructor {:?}", self), }; Pat { ty, span: DUMMY_SP, kind: Box::new(pat) } @@ -1122,7 +1125,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { // Use T as the sub pattern type of Box. Fields::from_single_pattern(wildcard_from_ty(substs.type_at(0))) } else { - let variant = &adt.variants[constructor.variant_index_for_adt(cx, adt)]; + let variant = &adt.variants[constructor.variant_index_for_adt(adt)]; // Whether we must not match the fields of this variant exhaustively. let is_non_exhaustive = variant.is_field_list_non_exhaustive() && !adt.did.is_local(); @@ -1170,9 +1173,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { } _ => bug!("bad slice pattern {:?} {:?}", constructor, ty), }, - ConstantValue(..) | Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive => { - Fields::empty() - } + Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque => Fields::empty(), }; debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret); ret @@ -2085,7 +2086,7 @@ fn is_useful_specialized<'p, 'tcx>( // We cache the result of `Fields::wildcards` because it is used a lot. let ctor_wild_subpatterns = Fields::wildcards(cx, &ctor, ty); let matrix = matrix.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns); - v.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns) + v.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns, true) .map(|v| is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false)) .map(|u| u.apply_constructor(cx, &ctor, ty, &ctor_wild_subpatterns)) .unwrap_or(NotUseful) @@ -2112,7 +2113,7 @@ fn pat_constructor<'tcx>( match value.ty.kind() { ty::Float(_) => Some(FloatRange(value, value, RangeEnd::Included)), ty::Ref(_, t, _) if t.is_str() => Some(Str(value)), - _ => Some(ConstantValue(value)), + _ => Some(Opaque), } } } @@ -2461,15 +2462,26 @@ fn specialize_one_pattern<'p, 'tcx>( pat: &'p Pat<'tcx>, constructor: &Constructor<'tcx>, ctor_wild_subpatterns: &Fields<'p, 'tcx>, + is_its_own_ctor: bool, // Whether `ctor` is known to be derived from `pat` ) -> Option> { if let NonExhaustive = constructor { - // Only a wildcard pattern can match the special extra constructor + // Only a wildcard pattern can match the special extra constructor. if !pat.is_wildcard() { return None; } return Some(Fields::empty()); } + if let Opaque = constructor { + // Only a wildcard pattern can match an opaque constant, unless we're specializing the + // value against its own constructor. + if is_its_own_ctor || pat.is_wildcard() { + return Some(Fields::empty()); + } else { + return None; + } + } + let result = match *pat.kind { PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern` @@ -2491,7 +2503,6 @@ fn specialize_one_pattern<'p, 'tcx>( PatKind::Constant { .. } | PatKind::Range { .. } => { match constructor { - Single => {} IntRange(ctor) => { let pat = IntRange::from_pat(cx.tcx, cx.param_env, pat)?; ctor.intersection(cx.tcx, &pat)?; @@ -2514,7 +2525,7 @@ fn specialize_one_pattern<'p, 'tcx>( return None; } } - ConstantValue(ctor_value) | Str(ctor_value) => { + Str(ctor_value) => { let pat_value = match *pat.kind { PatKind::Constant { value } => value, _ => span_bug!( @@ -2532,7 +2543,9 @@ fn specialize_one_pattern<'p, 'tcx>( } } _ => { - span_bug!(pat.span, "unexpected pattern {:?} with ctor {:?}", pat, constructor) + // If we reach here, we must be trying to inspect an opaque constant. Thus we skip + // the row. + return None; } } Some(Fields::empty()) diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 718ed78889f09..485e4cab87f82 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -158,6 +158,13 @@ crate enum PatKind<'tcx> { subpattern: Pat<'tcx>, }, + /// One of the following: + /// * `&str`, which will be handled as a string pattern and thus exhaustiveness + /// checking will detect if you use the same string twice in different patterns. + /// * integer, bool, char or float, which will be handled by exhaustivenes to cover exactly + /// its own value, similar to `&str`, but these values are much simpler. + /// * Opaque constants, that must not be matched structurally. So anything that does not derive + /// `PartialEq` and `Eq`. Constant { value: &'tcx ty::Const<'tcx>, }, diff --git a/src/test/ui/pattern/usefulness/consts-opaque.rs b/src/test/ui/pattern/usefulness/consts-opaque.rs index 928756723a6c9..761b293e9989b 100644 --- a/src/test/ui/pattern/usefulness/consts-opaque.rs +++ b/src/test/ui/pattern/usefulness/consts-opaque.rs @@ -106,8 +106,7 @@ fn main() { match QUUX { QUUX => {} - QUUX => {} // should not be emitting unreachable warning - //~^ ERROR unreachable pattern + QUUX => {} _ => {} } } diff --git a/src/test/ui/pattern/usefulness/consts-opaque.stderr b/src/test/ui/pattern/usefulness/consts-opaque.stderr index 07cdc1c95fad0..6d7e36e01f7f7 100644 --- a/src/test/ui/pattern/usefulness/consts-opaque.stderr +++ b/src/test/ui/pattern/usefulness/consts-opaque.stderr @@ -144,11 +144,5 @@ error: unreachable pattern LL | _ => {} // should not be emitting unreachable warning | ^ -error: unreachable pattern - --> $DIR/consts-opaque.rs:109:9 - | -LL | QUUX => {} // should not be emitting unreachable warning - | ^^^^ - -error: aborting due to 23 previous errors +error: aborting due to 22 previous errors From c4ae6c2bb95551afed4b87346cdf4ad4e4ba052c Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 18 Oct 2020 17:05:19 +0100 Subject: [PATCH 10/33] Add comment --- compiler/rustc_mir_build/src/thir/pattern/_match.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index fb068f21d8a69..a7ddca1b6b632 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -2113,6 +2113,9 @@ fn pat_constructor<'tcx>( match value.ty.kind() { ty::Float(_) => Some(FloatRange(value, value, RangeEnd::Included)), ty::Ref(_, t, _) if t.is_str() => Some(Str(value)), + // All constants that can be structurally matched have already been expanded + // into the corresponding `Pat`s by `const_to_pat`. Constants that remain are + // opaque. _ => Some(Opaque), } } From 13a5067061c14875bf1deda44a4740c08e872eb9 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 13 Oct 2020 16:20:53 +0200 Subject: [PATCH 11/33] Unignore test --- src/test/run-make-fulldeps/issue-36710/Makefile | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/run-make-fulldeps/issue-36710/Makefile b/src/test/run-make-fulldeps/issue-36710/Makefile index 4f93d97636e60..7eba52f3c24e8 100644 --- a/src/test/run-make-fulldeps/issue-36710/Makefile +++ b/src/test/run-make-fulldeps/issue-36710/Makefile @@ -1,7 +1,5 @@ -include ../tools.mk -# ignore-musl - all: foo $(call RUN,foo) From 0558e6eb9337960a1415e037ae7a44e3aa947479 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Wed, 14 Oct 2020 14:27:49 +0200 Subject: [PATCH 12/33] bootstrap: fall back to auto-detected CXX This allows us to use the C++ compiler configured via `CXX_target_triple` env vars --- src/bootstrap/cc_detect.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/cc_detect.rs b/src/bootstrap/cc_detect.rs index d50e4cf52697a..e750c2963dddc 100644 --- a/src/bootstrap/cc_detect.rs +++ b/src/bootstrap/cc_detect.rs @@ -129,7 +129,8 @@ pub fn find(build: &mut Build) { set_compiler(&mut cfg, Language::CPlusPlus, target, config, build); true } else { - false + // Use an auto-detected compiler (or one configured via `CXX_target_triple` env vars). + cfg.try_get_compiler().is_ok() }; // for VxWorks, record CXX compiler which will be used in lib.rs:linker() From 77a7ccf869db577c0d7262965c0ecdd1f50a49b8 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Wed, 14 Oct 2020 14:28:57 +0200 Subject: [PATCH 13/33] bootstrap: configure native toolchain for run-make This allows moving a lot of run-make-fulldeps tests to just run-make tests, and allows running those on target-only platforms --- src/bootstrap/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 33e85dc5e2a11..6c2c05ac7197e 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -1202,7 +1202,7 @@ note: if you're sure you want to do this, please open an issue as to why. In the // Only pass correct values for these flags for the `run-make` suite as it // requires that a C++ compiler was configured which isn't always the case. - if !builder.config.dry_run && suite == "run-make-fulldeps" { + if !builder.config.dry_run && matches!(suite, "run-make" | "run-make-fulldeps") { cmd.arg("--cc") .arg(builder.cc(target)) .arg("--cxx") From e36de6b2a1acc2826482f44a46b90678baccaaff Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Wed, 14 Oct 2020 14:29:57 +0200 Subject: [PATCH 14/33] Move issue-36710 test to run-make Somewhat hacky to reuse `tools.mk` like this, we should probably migrate most of them now --- src/test/{run-make-fulldeps => run-make}/issue-36710/Makefile | 2 +- src/test/{run-make-fulldeps => run-make}/issue-36710/foo.cpp | 0 src/test/{run-make-fulldeps => run-make}/issue-36710/foo.rs | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename src/test/{run-make-fulldeps => run-make}/issue-36710/Makefile (80%) rename src/test/{run-make-fulldeps => run-make}/issue-36710/foo.cpp (100%) rename src/test/{run-make-fulldeps => run-make}/issue-36710/foo.rs (100%) diff --git a/src/test/run-make-fulldeps/issue-36710/Makefile b/src/test/run-make/issue-36710/Makefile similarity index 80% rename from src/test/run-make-fulldeps/issue-36710/Makefile rename to src/test/run-make/issue-36710/Makefile index 7eba52f3c24e8..fe8061c013a68 100644 --- a/src/test/run-make-fulldeps/issue-36710/Makefile +++ b/src/test/run-make/issue-36710/Makefile @@ -1,4 +1,4 @@ --include ../tools.mk +include ../../run-make-fulldeps/tools.mk all: foo $(call RUN,foo) diff --git a/src/test/run-make-fulldeps/issue-36710/foo.cpp b/src/test/run-make/issue-36710/foo.cpp similarity index 100% rename from src/test/run-make-fulldeps/issue-36710/foo.cpp rename to src/test/run-make/issue-36710/foo.cpp diff --git a/src/test/run-make-fulldeps/issue-36710/foo.rs b/src/test/run-make/issue-36710/foo.rs similarity index 100% rename from src/test/run-make-fulldeps/issue-36710/foo.rs rename to src/test/run-make/issue-36710/foo.rs From a67160494cb9305e72aae5b367d9187f2f37e572 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Wed, 14 Oct 2020 16:25:10 +0200 Subject: [PATCH 15/33] Ignore test on WASM --- src/test/run-make/issue-36710/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/run-make/issue-36710/Makefile b/src/test/run-make/issue-36710/Makefile index fe8061c013a68..ddde078e8f84b 100644 --- a/src/test/run-make/issue-36710/Makefile +++ b/src/test/run-make/issue-36710/Makefile @@ -1,5 +1,7 @@ include ../../run-make-fulldeps/tools.mk +# ignore-wasm32 + all: foo $(call RUN,foo) From d7c7649f5b4688e73046d45293e4d046ade59135 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 16 Oct 2020 21:49:03 +0200 Subject: [PATCH 16/33] ignore-thumb --- src/test/run-make/issue-36710/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/run-make/issue-36710/Makefile b/src/test/run-make/issue-36710/Makefile index ddde078e8f84b..97a379de0b1b2 100644 --- a/src/test/run-make/issue-36710/Makefile +++ b/src/test/run-make/issue-36710/Makefile @@ -1,6 +1,7 @@ include ../../run-make-fulldeps/tools.mk # ignore-wasm32 +# ignore-thumb all: foo $(call RUN,foo) From 60594b1f0fd0fa69959e4fe56a61c01031916fe2 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Mon, 19 Oct 2020 16:37:53 +0200 Subject: [PATCH 17/33] Ignore on 32-bit targets --- src/test/run-make/issue-36710/Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/run-make/issue-36710/Makefile b/src/test/run-make/issue-36710/Makefile index 97a379de0b1b2..b7bf366c918e2 100644 --- a/src/test/run-make/issue-36710/Makefile +++ b/src/test/run-make/issue-36710/Makefile @@ -1,7 +1,6 @@ include ../../run-make-fulldeps/tools.mk -# ignore-wasm32 -# ignore-thumb +# ignore-32bit wrong/no cross compiler and sometimes we pass wrong gcc args (-m64) all: foo $(call RUN,foo) From 2780e35246f4f1b44fc1e17bc2f99cfbfedef127 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Mon, 19 Oct 2020 22:31:11 +0200 Subject: [PATCH 18/33] Throw core::panic!("message") as &str instead of String. This makes it consistent with std::panic!("message"), which also throws a &str, not a String. --- library/std/src/lib.rs | 1 + library/std/src/panicking.rs | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 30e7a7f3c3b10..3da0ebdd4982a 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -259,6 +259,7 @@ #![feature(exhaustive_patterns)] #![feature(extend_one)] #![feature(external_doc)] +#![feature(fmt_as_str)] #![feature(fn_traits)] #![feature(format_args_nl)] #![feature(gen_future)] diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 8dceb12de87b8..221ae809e23a2 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -478,10 +478,26 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { } } + struct StrPanicPayload(&'static str); + + unsafe impl BoxMeUp for StrPanicPayload { + fn take_box(&mut self) -> *mut (dyn Any + Send) { + Box::into_raw(Box::new(self.0)) + } + + fn get(&mut self) -> &(dyn Any + Send) { + &self.0 + } + } + let loc = info.location().unwrap(); // The current implementation always returns Some let msg = info.message().unwrap(); // The current implementation always returns Some crate::sys_common::backtrace::__rust_end_short_backtrace(move || { - rust_panic_with_hook(&mut PanicPayload::new(msg), info.message(), loc); + if let Some(msg) = msg.as_str() { + rust_panic_with_hook(&mut StrPanicPayload(msg), info.message(), loc); + } else { + rust_panic_with_hook(&mut PanicPayload::new(msg), info.message(), loc); + } }) } From 9890217c0eedcbe4742e412ea59e851ecd500bf5 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Mon, 19 Oct 2020 22:36:45 +0200 Subject: [PATCH 19/33] Fix ui test for updated core::panic behaviour. It now throws a &str instead of a String. --- src/test/ui/intrinsics/panic-uninitialized-zeroed.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs index 24474cabf1e37..4a91198ab9f6f 100644 --- a/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs +++ b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs @@ -53,8 +53,8 @@ enum LR_NonZero { fn test_panic_msg(op: impl (FnOnce() -> T) + panic::UnwindSafe, msg: &str) { let err = panic::catch_unwind(op).err(); assert_eq!( - err.as_ref().and_then(|a| a.downcast_ref::()).map(|s| &**s), - Some(msg) + err.as_ref().and_then(|a| a.downcast_ref::<&str>()), + Some(&msg) ); } From d80f127a75017dcdc91f4535b26a668976e2cfc7 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Mon, 19 Oct 2020 23:58:42 +0200 Subject: [PATCH 20/33] Avoid panic_bounds_check in fmt::write. Writing any fmt::Arguments would trigger the inclusion of usize formatting and padding code in the resulting binary, because indexing used in fmt::write would generate code using panic_bounds_check, which prints the index and length. These bounds checks are not necessary, as fmt::Arguments never contains any out-of-bounds indexes. This change replaces them with unsafe get_unchecked, to reduce the amount of generated code, which is especially important for embedded targets. --- library/core/src/fmt/mod.rs | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 0963c6d6cd7ea..04edf4611ecc3 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -1082,7 +1082,9 @@ pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result { // a string piece. for (arg, piece) in fmt.iter().zip(args.pieces.iter()) { formatter.buf.write_str(*piece)?; - run(&mut formatter, arg, &args.args)?; + // SAFETY: arg and args.args come from the same Arguments, + // which guarantees the indexes are always within bounds. + unsafe { run(&mut formatter, arg, &args.args) }?; idx += 1; } } @@ -1096,25 +1098,36 @@ pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result { Ok(()) } -fn run(fmt: &mut Formatter<'_>, arg: &rt::v1::Argument, args: &[ArgumentV1<'_>]) -> Result { +unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::v1::Argument, args: &[ArgumentV1<'_>]) -> Result { fmt.fill = arg.format.fill; fmt.align = arg.format.align; fmt.flags = arg.format.flags; - fmt.width = getcount(args, &arg.format.width); - fmt.precision = getcount(args, &arg.format.precision); + // SAFETY: arg and args come from the same Arguments, + // which guarantees the indexes are always within bounds. + unsafe { + fmt.width = getcount(args, &arg.format.width); + fmt.precision = getcount(args, &arg.format.precision); + } // Extract the correct argument - let value = args[arg.position]; + + // SAFETY: arg and args come from the same Arguments, + // which guarantees its index is always within bounds. + let value = unsafe { args.get_unchecked(arg.position) }; // Then actually do some printing (value.formatter)(value.value, fmt) } -fn getcount(args: &[ArgumentV1<'_>], cnt: &rt::v1::Count) -> Option { +unsafe fn getcount(args: &[ArgumentV1<'_>], cnt: &rt::v1::Count) -> Option { match *cnt { rt::v1::Count::Is(n) => Some(n), rt::v1::Count::Implied => None, - rt::v1::Count::Param(i) => args[i].as_usize(), + rt::v1::Count::Param(i) => { + // SAFETY: cnt and args come from the same Arguments, + // which guarantees this index is always within bounds. + unsafe { args.get_unchecked(i).as_usize() } + } } } From 5bfd3e7259caa4b94f9cfcf2af9ccc1d8420e286 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 20 Oct 2020 00:06:00 +0100 Subject: [PATCH 21/33] Accidentally fixed #78071 --- .../ui/pattern/usefulness/consts-opaque.rs | 12 +++-- .../pattern/usefulness/consts-opaque.stderr | 48 +++++++++++-------- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/src/test/ui/pattern/usefulness/consts-opaque.rs b/src/test/ui/pattern/usefulness/consts-opaque.rs index 761b293e9989b..f87f96e34fccd 100644 --- a/src/test/ui/pattern/usefulness/consts-opaque.rs +++ b/src/test/ui/pattern/usefulness/consts-opaque.rs @@ -44,11 +44,13 @@ fn main() { //~^ ERROR unreachable pattern } - // FIXME: this causes an ICE (https://github.com/rust-lang/rust/issues/78071) - //match FOO_REF_REF { - // FOO_REF_REF => {} - // Foo(_) => {} - //} + // This used to cause an ICE (https://github.com/rust-lang/rust/issues/78071) + match FOO_REF_REF { + FOO_REF_REF => {} + //~^ WARNING must be annotated with `#[derive(PartialEq, Eq)]` + //~| WARNING this was previously accepted by the compiler but is being phased out + Foo(_) => {} + } match BAR { Bar => {} diff --git a/src/test/ui/pattern/usefulness/consts-opaque.stderr b/src/test/ui/pattern/usefulness/consts-opaque.stderr index 6d7e36e01f7f7..f10166d5a3580 100644 --- a/src/test/ui/pattern/usefulness/consts-opaque.stderr +++ b/src/test/ui/pattern/usefulness/consts-opaque.stderr @@ -28,14 +28,24 @@ error: unreachable pattern LL | Foo(_) => {} // should not be emitting unreachable warning | ^^^^^^ +warning: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/consts-opaque.rs:49:9 + | +LL | FOO_REF_REF => {} + | ^^^^^^^^^^^ + | + = note: `#[warn(indirect_structural_match)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #62411 + error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/consts-opaque.rs:55:9 + --> $DIR/consts-opaque.rs:57:9 | LL | BAR => {} // should not be emitting unreachable warning | ^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:55:9 + --> $DIR/consts-opaque.rs:57:9 | LL | Bar => {} | --- matches any value @@ -43,7 +53,7 @@ LL | BAR => {} // should not be emitting unreachable warning | ^^^ unreachable pattern error: unreachable pattern - --> $DIR/consts-opaque.rs:58:9 + --> $DIR/consts-opaque.rs:60:9 | LL | Bar => {} | --- matches any value @@ -52,19 +62,19 @@ LL | _ => {} | ^ unreachable pattern error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/consts-opaque.rs:63:9 + --> $DIR/consts-opaque.rs:65:9 | LL | BAR => {} | ^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:65:9 + --> $DIR/consts-opaque.rs:67:9 | LL | Bar => {} // should not be emitting unreachable warning | ^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:67:9 + --> $DIR/consts-opaque.rs:69:9 | LL | Bar => {} // should not be emitting unreachable warning | --- matches any value @@ -73,76 +83,76 @@ LL | _ => {} | ^ unreachable pattern error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/consts-opaque.rs:72:9 + --> $DIR/consts-opaque.rs:74:9 | LL | BAR => {} | ^^^ error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/consts-opaque.rs:74:9 + --> $DIR/consts-opaque.rs:76:9 | LL | BAR => {} // should not be emitting unreachable warning | ^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:74:9 + --> $DIR/consts-opaque.rs:76:9 | LL | BAR => {} // should not be emitting unreachable warning | ^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:77:9 + --> $DIR/consts-opaque.rs:79:9 | LL | _ => {} // should not be emitting unreachable warning | ^ error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/consts-opaque.rs:82:9 + --> $DIR/consts-opaque.rs:84:9 | LL | BAZ => {} | ^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:84:9 + --> $DIR/consts-opaque.rs:86:9 | LL | Baz::Baz1 => {} // should not be emitting unreachable warning | ^^^^^^^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:86:9 + --> $DIR/consts-opaque.rs:88:9 | LL | _ => {} | ^ error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/consts-opaque.rs:92:9 + --> $DIR/consts-opaque.rs:94:9 | LL | BAZ => {} | ^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:94:9 + --> $DIR/consts-opaque.rs:96:9 | LL | _ => {} | ^ error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/consts-opaque.rs:99:9 + --> $DIR/consts-opaque.rs:101:9 | LL | BAZ => {} | ^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:101:9 + --> $DIR/consts-opaque.rs:103:9 | LL | Baz::Baz2 => {} // should not be emitting unreachable warning | ^^^^^^^^^ error: unreachable pattern - --> $DIR/consts-opaque.rs:103:9 + --> $DIR/consts-opaque.rs:105:9 | LL | _ => {} // should not be emitting unreachable warning | ^ -error: aborting due to 22 previous errors +error: aborting due to 22 previous errors; 1 warning emitted From ea24395617745dd0483a7c4114da04b114a7d3d8 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 20 Oct 2020 20:20:06 +0200 Subject: [PATCH 22/33] Add debug_asserts for the unsafe indexing in fmt::write. --- library/core/src/fmt/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 04edf4611ecc3..cc84bf14a33a5 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -1110,7 +1110,7 @@ unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::v1::Argument, args: &[ArgumentV } // Extract the correct argument - + debug_assert!(arg.position < args.len()); // SAFETY: arg and args come from the same Arguments, // which guarantees its index is always within bounds. let value = unsafe { args.get_unchecked(arg.position) }; @@ -1124,6 +1124,7 @@ unsafe fn getcount(args: &[ArgumentV1<'_>], cnt: &rt::v1::Count) -> Option Some(n), rt::v1::Count::Implied => None, rt::v1::Count::Param(i) => { + debug_assert!(i < args.len()); // SAFETY: cnt and args come from the same Arguments, // which guarantees this index is always within bounds. unsafe { args.get_unchecked(i).as_usize() } From 5948e62f3443df626cc803fd3a08bc24e42209e8 Mon Sep 17 00:00:00 2001 From: est31 Date: Tue, 20 Oct 2020 18:38:54 +0200 Subject: [PATCH 23/33] Sync LLVM submodule if it has been initialized Since having enabled the download-ci-llvm option, and having rebased on top of f05b47ccdfa63f8b4b9fb47a9aa92381801d3ff1, I've noticed that I had to update the llvm-project submodule manually if it was checked out. Orignally, the submodule update logic was introduced to reduce the friction for contributors to manage the submodules, or in other words, to prevent getting PRs that have unwanted submodule rollbacks because the contributors didn't run git submodule update. This commit adds logic to ensure there is no inadvertent LLVM submodule rollback in a PR if download-ci-llvm (or llvm-config) is enabled. It will detect whether the llvm-project submodule is initialized, and if so, update it in any case. If it is not initialized, behaviour is kept to not do any update/initialization. An alternative to the chosen implementation would be to not pass the --init command line arg to `git submodule update` for the src/llvm-project submodule. This would show a confusing error message however on all builds with an uninitialized repo. We could pass the --silent param, but we still want it to print something if it is initialized and has to update something. So we just do a manual check for whether the submodule is initialized. --- src/bootstrap/bootstrap.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index ce37adeb28c93..a14c12a39e5e4 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -891,10 +891,15 @@ def update_submodules(self): ).decode(default_encoding).splitlines()] filtered_submodules = [] submodules_names = [] + llvm_checked_out = os.path.exists(os.path.join(self.rust_root, "src/llvm-project/.git")) for module in submodules: if module.endswith("llvm-project"): + # Don't sync the llvm-project submodule either if an external LLVM + # was provided, or if we are downloading LLVM. Also, if the + # submodule has been initialized already, sync it anyways so that + # it doesn't mess up contributor pull requests. if self.get_toml('llvm-config') or self.downloading_llvm(): - if self.get_toml('lld') != 'true': + if self.get_toml('lld') != 'true' and not llvm_checked_out: continue check = self.check_submodule(module, slow_submodules) filtered_submodules.append((module, check)) From 356d5b5b2addd6f34b0d6809635dd08a4712b126 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 20 Oct 2020 20:20:31 +0200 Subject: [PATCH 24/33] Add test to check for fmt::write bloat. It checks that fmt::write by itself doesn't pull in any panicking or or display code. --- src/test/run-make/fmt-write-bloat/Makefile | 7 +++++ src/test/run-make/fmt-write-bloat/main.rs | 32 ++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 src/test/run-make/fmt-write-bloat/Makefile create mode 100644 src/test/run-make/fmt-write-bloat/main.rs diff --git a/src/test/run-make/fmt-write-bloat/Makefile b/src/test/run-make/fmt-write-bloat/Makefile new file mode 100644 index 0000000000000..4227adb0d307c --- /dev/null +++ b/src/test/run-make/fmt-write-bloat/Makefile @@ -0,0 +1,7 @@ +-include ../../run-make-fulldeps/tools.mk + +NM=nm + +all: main.rs + $(RUSTC) $< -O + $(NM) $(call RUN_BINFILE,main) | $(CGREP) -v panicking panic_fmt panic_bounds_check pad_integral Display Debug diff --git a/src/test/run-make/fmt-write-bloat/main.rs b/src/test/run-make/fmt-write-bloat/main.rs new file mode 100644 index 0000000000000..e86c48014c3aa --- /dev/null +++ b/src/test/run-make/fmt-write-bloat/main.rs @@ -0,0 +1,32 @@ +#![feature(lang_items)] +#![feature(start)] +#![no_std] + +use core::fmt; +use core::fmt::Write; + +#[link(name = "c")] +extern "C" {} + +struct Dummy; + +impl fmt::Write for Dummy { + #[inline(never)] + fn write_str(&mut self, _: &str) -> fmt::Result { + Ok(()) + } +} + +#[start] +fn main(_: isize, _: *const *const u8) -> isize { + let _ = writeln!(Dummy, "Hello World"); + 0 +} + +#[lang = "eh_personality"] +fn eh_personality() {} + +#[panic_handler] +fn panic(_: &core::panic::PanicInfo) -> ! { + loop {} +} From e852a4abf01f605aa71cd529887c17c4c64f33ed Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 20 Oct 2020 16:36:46 -0700 Subject: [PATCH 25/33] Update cargo --- Cargo.lock | 15 ++++++++++++++- src/tools/cargo | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0d2170a992747..034e7e34eb39e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -310,7 +310,7 @@ dependencies = [ "crypto-hash", "curl", "curl-sys", - "env_logger 0.7.1", + "env_logger 0.8.1", "filetime", "flate2", "fwdansi", @@ -1035,6 +1035,19 @@ dependencies = [ "termcolor", ] +[[package]] +name = "env_logger" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54532e3223c5af90a6a757c90b5c5521564b07e5e7a958681bcd2afad421cdcd" +dependencies = [ + "atty", + "humantime 2.0.1", + "log", + "regex", + "termcolor", +] + [[package]] name = "error_index_generator" version = "0.0.0" diff --git a/src/tools/cargo b/src/tools/cargo index 79b397d72c557..dd83ae55c871d 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 79b397d72c557eb6444a2ba0dc00a211a226a35a +Subproject commit dd83ae55c871d94f060524656abab62ec40b4c40 From 52640f29257574877ae5bcd38f52c8793a1ff31c Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Sat, 30 May 2020 15:02:32 -0400 Subject: [PATCH 26/33] [mir-opt] Allow debuginfo to be generated for a constant or a Place Prior to this commit, debuginfo was always generated by mapping a name to a Place. This has the side-effect that `SimplifyLocals` cannot remove locals that are only used for debuginfo because their other uses have been const-propagated. To allow these locals to be removed, we now allow debuginfo to point to a constant value. The `ConstProp` pass detects when debuginfo points to a local with a known constant value and replaces it with the value. This allows the later `SimplifyLocals` pass to remove the local. --- .../src/debuginfo/metadata.rs | 5 +- .../rustc_codegen_ssa/src/mir/constant.rs | 4 +- .../rustc_codegen_ssa/src/mir/debuginfo.rs | 100 ++++++++++----- compiler/rustc_codegen_ssa/src/mir/mod.rs | 2 +- compiler/rustc_middle/src/mir/mod.rs | 21 +++- compiler/rustc_middle/src/mir/visit.rs | 16 ++- compiler/rustc_mir/src/borrow_check/mod.rs | 26 ++-- .../src/transform/const_debuginfo.rs | 98 +++++++++++++++ compiler/rustc_mir/src/transform/mod.rs | 2 + .../rustc_mir/src/transform/simplify_try.rs | 41 ++++--- compiler/rustc_mir/src/util/graphviz.rs | 2 +- compiler/rustc_mir/src/util/pretty.rs | 2 +- .../rustc_mir_build/src/build/matches/mod.rs | 4 +- compiler/rustc_mir_build/src/build/mod.rs | 6 +- src/test/codegen/lifetime_start_end.rs | 8 +- .../incremental/hashes/let_expressions.rs | 4 +- .../const_debuginfo.main.ConstDebugInfo.diff | 115 ++++++++++++++++++ src/test/mir-opt/const_debuginfo.rs | 24 ++++ ...riable.main.SimplifyLocals.after.32bit.mir | 18 +-- ...riable.main.SimplifyLocals.after.64bit.mir | 18 +-- ...hange_loop_body.PreCodegen.after.32bit.mir | 14 +-- ...hange_loop_body.PreCodegen.after.64bit.mir | 14 +-- 22 files changed, 410 insertions(+), 134 deletions(-) create mode 100644 compiler/rustc_mir/src/transform/const_debuginfo.rs create mode 100644 src/test/mir-opt/const_debuginfo.main.ConstDebugInfo.diff create mode 100644 src/test/mir-opt/const_debuginfo.rs diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 5587e6ead1dbb..81f0cc6e862a0 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -1416,10 +1416,11 @@ fn generator_layout_and_saved_local_names( let state_arg = mir::Local::new(1); for var in &body.var_debug_info { - if var.place.local != state_arg { + let place = if let mir::VarDebugInfoContents::Place(p) = var.value { p } else { continue }; + if place.local != state_arg { continue; } - match var.place.projection[..] { + match place.projection[..] { [ // Deref of the `Pin<&mut Self>` state argument. mir::ProjectionElem::Field(..), diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index 4943e279c7e05..61f831f25ab06 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -11,7 +11,7 @@ use super::FunctionCx; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub fn eval_mir_constant_to_operand( - &mut self, + &self, bx: &mut Bx, constant: &mir::Constant<'tcx>, ) -> Result, ErrorHandled> { @@ -21,7 +21,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } pub fn eval_mir_constant( - &mut self, + &self, constant: &mir::Constant<'tcx>, ) -> Result, ErrorHandled> { match self.monomorphize(&constant.literal).val { diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 26a646b029367..8ab081e8c3fff 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -9,7 +9,7 @@ use rustc_span::symbol::{kw, Symbol}; use rustc_span::{BytePos, Span}; use rustc_target::abi::{LayoutOf, Size}; -use super::operand::OperandValue; +use super::operand::{OperandRef, OperandValue}; use super::place::PlaceRef; use super::{FunctionCx, LocalRef}; @@ -111,6 +111,24 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } + fn spill_operand_to_stack( + operand: &OperandRef<'tcx, Bx::Value>, + name: Option, + bx: &mut Bx, + ) -> PlaceRef<'tcx, Bx::Value> { + // "Spill" the value onto the stack, for debuginfo, + // without forcing non-debuginfo uses of the local + // to also load from the stack every single time. + // FIXME(#68817) use `llvm.dbg.value` instead, + // at least for the cases which LLVM handles correctly. + let spill_slot = PlaceRef::alloca(bx, operand.layout); + if let Some(name) = name { + bx.set_var_name(spill_slot.llval, &(name + ".dbg.spill")); + } + operand.val.store(bx, spill_slot); + spill_slot + } + /// Apply debuginfo and/or name, after creating the `alloca` for a local, /// or initializing the local with an operand (whichever applies). pub fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) { @@ -225,17 +243,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return; } - // "Spill" the value onto the stack, for debuginfo, - // without forcing non-debuginfo uses of the local - // to also load from the stack every single time. - // FIXME(#68817) use `llvm.dbg.value` instead, - // at least for the cases which LLVM handles correctly. - let spill_slot = PlaceRef::alloca(bx, operand.layout); - if let Some(name) = name { - bx.set_var_name(spill_slot.llval, &(name + ".dbg.spill")); - } - operand.val.store(bx, spill_slot); - spill_slot + Self::spill_operand_to_stack(operand, name, bx) } LocalRef::Place(place) => *place, @@ -310,6 +318,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { /// Partition all `VarDebugInfo` in `self.mir`, by their base `Local`. pub fn compute_per_local_var_debug_info( &self, + bx: &mut Bx, ) -> Option>>> { let full_debug_info = self.cx.sess().opts.debuginfo == DebugInfo::Full; @@ -324,22 +333,30 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } else { (None, var.source_info.span) }; + let (var_ty, var_kind) = match var.value { + mir::VarDebugInfoContents::Place(place) => { + let var_ty = self.monomorphized_place_ty(place.as_ref()); + let var_kind = if self.mir.local_kind(place.local) == mir::LocalKind::Arg + && place.projection.is_empty() + && var.source_info.scope == mir::OUTERMOST_SOURCE_SCOPE + { + let arg_index = place.local.index() - 1; + + // FIXME(eddyb) shouldn't `ArgumentVariable` indices be + // offset in closures to account for the hidden environment? + // Also, is this `+ 1` needed at all? + VariableKind::ArgumentVariable(arg_index + 1) + } else { + VariableKind::LocalVariable + }; + (var_ty, var_kind) + } + mir::VarDebugInfoContents::Const(c) => { + let ty = self.monomorphize(&c.literal.ty); + (ty, VariableKind::LocalVariable) + } + }; let dbg_var = scope.map(|scope| { - let place = var.place; - let var_ty = self.monomorphized_place_ty(place.as_ref()); - let var_kind = if self.mir.local_kind(place.local) == mir::LocalKind::Arg - && place.projection.is_empty() - && var.source_info.scope == mir::OUTERMOST_SOURCE_SCOPE - { - let arg_index = place.local.index() - 1; - - // FIXME(eddyb) shouldn't `ArgumentVariable` indices be - // offset in closures to account for the hidden environment? - // Also, is this `+ 1` needed at all? - VariableKind::ArgumentVariable(arg_index + 1) - } else { - VariableKind::LocalVariable - }; self.cx.create_dbg_var( self.debug_context.as_ref().unwrap(), var.name, @@ -350,12 +367,29 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ) }); - per_local[var.place.local].push(PerLocalVarDebugInfo { - name: var.name, - source_info: var.source_info, - dbg_var, - projection: var.place.projection, - }); + match var.value { + mir::VarDebugInfoContents::Place(place) => { + per_local[place.local].push(PerLocalVarDebugInfo { + name: var.name, + source_info: var.source_info, + dbg_var, + projection: place.projection, + }); + } + mir::VarDebugInfoContents::Const(c) => { + if let (Some(scope), Some(dbg_var)) = (scope, dbg_var) { + if let Ok(operand) = self.eval_mir_constant_to_operand(bx, &c) { + let base = Self::spill_operand_to_stack( + &operand, + Some(var.name.to_string()), + bx, + ); + + bx.dbg_var_addr(dbg_var, scope, base.llval, Size::ZERO, &[], span); + } + } + } + } } Some(per_local) } diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index bff263567bf6b..295bfa7469c82 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -190,7 +190,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( caller_location: None, }; - fx.per_local_var_debug_info = fx.compute_per_local_var_debug_info(); + fx.per_local_var_debug_info = fx.compute_per_local_var_debug_info(&mut bx); for const_ in &mir.required_consts { if let Err(err) = fx.eval_mir_constant(const_) { diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index b38efedbf607e..a8cfb340e99c0 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1074,6 +1074,23 @@ impl<'tcx> LocalDecl<'tcx> { } } +#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +pub enum VarDebugInfoContents<'tcx> { + /// NOTE(eddyb) There's an unenforced invariant that this `Place` is + /// based on a `Local`, not a `Static`, and contains no indexing. + Place(Place<'tcx>), + Const(Constant<'tcx>), +} + +impl<'tcx> Debug for VarDebugInfoContents<'tcx> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + match self { + VarDebugInfoContents::Const(c) => write!(fmt, "{}", c), + VarDebugInfoContents::Place(p) => write!(fmt, "{:?}", p), + } + } +} + /// Debug information pertaining to a user variable. #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)] pub struct VarDebugInfo<'tcx> { @@ -1085,9 +1102,7 @@ pub struct VarDebugInfo<'tcx> { pub source_info: SourceInfo, /// Where the data for this user variable is to be found. - /// NOTE(eddyb) There's an unenforced invariant that this `Place` is - /// based on a `Local`, not a `Static`, and contains no indexing. - pub place: Place<'tcx>, + pub value: VarDebugInfoContents<'tcx>, } /////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 58dd0bc00d204..91b4de45004d2 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -796,16 +796,20 @@ macro_rules! make_mir_visitor { let VarDebugInfo { name: _, source_info, - place, + value, } = var_debug_info; self.visit_source_info(source_info); let location = START_BLOCK.start_location(); - self.visit_place( - place, - PlaceContext::NonUse(NonUseContext::VarDebugInfo), - location, - ); + match value { + VarDebugInfoContents::Const(c) => self.visit_constant(c, location), + VarDebugInfoContents::Place(place) => + self.visit_place( + place, + PlaceContext::NonUse(NonUseContext::VarDebugInfo), + location + ), + } } fn super_source_scope(&mut self, diff --git a/compiler/rustc_mir/src/borrow_check/mod.rs b/compiler/rustc_mir/src/borrow_check/mod.rs index 4b7af271baef0..19f4263fb16ff 100644 --- a/compiler/rustc_mir/src/borrow_check/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/mod.rs @@ -11,7 +11,7 @@ use rustc_index::vec::IndexVec; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_middle::mir::{ traversal, Body, ClearCrossCrate, Local, Location, Mutability, Operand, Place, PlaceElem, - PlaceRef, + PlaceRef, VarDebugInfoContents, }; use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind}; use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind}; @@ -133,19 +133,21 @@ fn do_mir_borrowck<'a, 'tcx>( let mut local_names = IndexVec::from_elem(None, &input_body.local_decls); for var_debug_info in &input_body.var_debug_info { - if let Some(local) = var_debug_info.place.as_local() { - if let Some(prev_name) = local_names[local] { - if var_debug_info.name != prev_name { - span_bug!( - var_debug_info.source_info.span, - "local {:?} has many names (`{}` vs `{}`)", - local, - prev_name, - var_debug_info.name - ); + if let VarDebugInfoContents::Place(place) = var_debug_info.value { + if let Some(local) = place.as_local() { + if let Some(prev_name) = local_names[local] { + if var_debug_info.name != prev_name { + span_bug!( + var_debug_info.source_info.span, + "local {:?} has many names (`{}` vs `{}`)", + local, + prev_name, + var_debug_info.name + ); + } } + local_names[local] = Some(var_debug_info.name); } - local_names[local] = Some(var_debug_info.name); } } diff --git a/compiler/rustc_mir/src/transform/const_debuginfo.rs b/compiler/rustc_mir/src/transform/const_debuginfo.rs new file mode 100644 index 0000000000000..719075f94823c --- /dev/null +++ b/compiler/rustc_mir/src/transform/const_debuginfo.rs @@ -0,0 +1,98 @@ +//! Finds locals which are assigned once to a const and unused except for debuginfo and converts +//! their debuginfo to use the const directly, allowing the local to be removed. + +use rustc_middle::{ + mir::{ + visit::{PlaceContext, Visitor}, + Body, Constant, Local, Location, Operand, Rvalue, StatementKind, VarDebugInfoContents, + }, + ty::TyCtxt, +}; + +use crate::transform::MirPass; +use rustc_index::{bit_set::BitSet, vec::IndexVec}; + +pub struct ConstDebugInfo; + +impl<'tcx> MirPass<'tcx> for ConstDebugInfo { + fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + trace!("running ConstDebugInfo on {:?}", body.source); + + for (local, constant) in find_optimization_oportunities(body) { + for debuginfo in &mut body.var_debug_info { + if let VarDebugInfoContents::Place(p) = debuginfo.value { + if p.local == local && p.projection.is_empty() { + trace!( + "changing debug info for {:?} from place {:?} to constant {:?}", + debuginfo.name, + p, + constant + ); + debuginfo.value = VarDebugInfoContents::Const(constant); + } + } + } + } + } +} + +struct LocalUseVisitor { + local_mutating_uses: IndexVec, + local_assignment_locations: IndexVec>, +} + +fn find_optimization_oportunities<'tcx>(body: &Body<'tcx>) -> Vec<(Local, Constant<'tcx>)> { + let mut visitor = LocalUseVisitor { + local_mutating_uses: IndexVec::from_elem(0, &body.local_decls), + local_assignment_locations: IndexVec::from_elem(None, &body.local_decls), + }; + + visitor.visit_body(body); + + let mut locals_to_debuginfo = BitSet::new_empty(body.local_decls.len()); + for debuginfo in &body.var_debug_info { + if let VarDebugInfoContents::Place(p) = debuginfo.value { + if let Some(l) = p.as_local() { + locals_to_debuginfo.insert(l); + } + } + } + + let mut eligable_locals = Vec::new(); + for (local, mutating_uses) in visitor.local_mutating_uses.drain_enumerated(..) { + if mutating_uses != 1 || !locals_to_debuginfo.contains(local) { + continue; + } + + if let Some(location) = visitor.local_assignment_locations[local] { + let bb = &body[location.block]; + + // The value is assigned as the result of a call, not a constant + if bb.statements.len() == location.statement_index { + continue; + } + + if let StatementKind::Assign(box (p, Rvalue::Use(Operand::Constant(box c)))) = + &bb.statements[location.statement_index].kind + { + if let Some(local) = p.as_local() { + eligable_locals.push((local, *c)); + } + } + } + } + + eligable_locals +} + +impl<'tcx> Visitor<'tcx> for LocalUseVisitor { + fn visit_local(&mut self, local: &Local, context: PlaceContext, location: Location) { + if context.is_mutating_use() { + self.local_mutating_uses[*local] += 1; + + if context.is_place_assignment() { + self.local_assignment_locations[*local] = Some(location); + } + } + } +} diff --git a/compiler/rustc_mir/src/transform/mod.rs b/compiler/rustc_mir/src/transform/mod.rs index 20b8c90a9dcad..51f2485a09d2f 100644 --- a/compiler/rustc_mir/src/transform/mod.rs +++ b/compiler/rustc_mir/src/transform/mod.rs @@ -21,6 +21,7 @@ pub mod check_consts; pub mod check_packed_ref; pub mod check_unsafety; pub mod cleanup_post_borrowck; +pub mod const_debuginfo; pub mod const_prop; pub mod deaggregator; pub mod dest_prop; @@ -404,6 +405,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &remove_noop_landing_pads::RemoveNoopLandingPads, &simplify::SimplifyCfg::new("final"), &nrvo::RenameReturnPlace, + &const_debuginfo::ConstDebugInfo, &simplify::SimplifyLocals, &multiple_return_terminators::MultipleReturnTerminators, ]; diff --git a/compiler/rustc_mir/src/transform/simplify_try.rs b/compiler/rustc_mir/src/transform/simplify_try.rs index 27bb1def726e1..bea95bf43d21e 100644 --- a/compiler/rustc_mir/src/transform/simplify_try.rs +++ b/compiler/rustc_mir/src/transform/simplify_try.rs @@ -246,14 +246,19 @@ fn get_arm_identity_info<'a, 'tcx>( tmp_assigned_vars.insert(*r); } - let dbg_info_to_adjust: Vec<_> = - debug_info - .iter() - .enumerate() - .filter_map(|(i, var_info)| { - if tmp_assigned_vars.contains(var_info.place.local) { Some(i) } else { None } - }) - .collect(); + let dbg_info_to_adjust: Vec<_> = debug_info + .iter() + .enumerate() + .filter_map(|(i, var_info)| { + if let VarDebugInfoContents::Place(p) = var_info.value { + if tmp_assigned_vars.contains(p.local) { + return Some(i); + } + } + + None + }) + .collect(); Some(ArmIdentityInfo { local_temp_0: local_tmp_s0, @@ -340,9 +345,11 @@ fn optimization_applies<'tcx>( // Check that debug info only points to full Locals and not projections. for dbg_idx in &opt_info.dbg_info_to_adjust { let dbg_info = &var_debug_info[*dbg_idx]; - if !dbg_info.place.projection.is_empty() { - trace!("NO: debug info for {:?} had a projection {:?}", dbg_info.name, dbg_info.place); - return false; + if let VarDebugInfoContents::Place(p) = dbg_info.value { + if !p.projection.is_empty() { + trace!("NO: debug info for {:?} had a projection {:?}", dbg_info.name, p); + return false; + } } } @@ -423,9 +430,15 @@ impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity { // Fix the debug info to point to the right local for dbg_index in opt_info.dbg_info_to_adjust { let dbg_info = &mut debug_info[dbg_index]; - assert!(dbg_info.place.projection.is_empty()); - dbg_info.place.local = opt_info.local_0; - dbg_info.place.projection = opt_info.dbg_projection; + assert!( + matches!(dbg_info.value, VarDebugInfoContents::Place(_)), + "value was not a Place" + ); + if let VarDebugInfoContents::Place(p) = &mut dbg_info.value { + assert!(p.projection.is_empty()); + p.local = opt_info.local_0; + p.projection = opt_info.dbg_projection; + } } trace!("block is now {:?}", bb.statements); diff --git a/compiler/rustc_mir/src/util/graphviz.rs b/compiler/rustc_mir/src/util/graphviz.rs index e04f07774ed0b..4b2d68aea9e12 100644 --- a/compiler/rustc_mir/src/util/graphviz.rs +++ b/compiler/rustc_mir/src/util/graphviz.rs @@ -218,7 +218,7 @@ fn write_graph_label<'tcx, W: Write>( w, r#"debug {} => {};
"#, var_debug_info.name, - escape(&var_debug_info.place) + escape(&var_debug_info.value), )?; } diff --git a/compiler/rustc_mir/src/util/pretty.rs b/compiler/rustc_mir/src/util/pretty.rs index bd7c25bf25043..d6458443cb3cf 100644 --- a/compiler/rustc_mir/src/util/pretty.rs +++ b/compiler/rustc_mir/src/util/pretty.rs @@ -494,7 +494,7 @@ fn write_scope_tree( let indented_debug_info = format!( "{0:1$}debug {2} => {3:?};", - INDENT, indent, var_debug_info.name, var_debug_info.place, + INDENT, indent, var_debug_info.name, var_debug_info.value, ); writeln!( diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index b7bd67fea0679..0737d5f2cb737 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -1992,7 +1992,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.var_debug_info.push(VarDebugInfo { name, source_info: debug_source_info, - place: for_arm_body.into(), + value: VarDebugInfoContents::Place(for_arm_body.into()), }); let locals = if has_guard.0 { let ref_for_guard = self.local_decls.push(LocalDecl::<'tcx> { @@ -2011,7 +2011,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.var_debug_info.push(VarDebugInfo { name, source_info: debug_source_info, - place: ref_for_guard.into(), + value: VarDebugInfoContents::Place(ref_for_guard.into()), }); LocalsForNode::ForGuard { ref_for_guard, for_arm_body } } else { diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 899fc647493a0..498b4053fccc7 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -796,7 +796,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.var_debug_info.push(VarDebugInfo { name: ident.name, source_info, - place: arg_local.into(), + value: VarDebugInfoContents::Place(arg_local.into()), }); } } @@ -860,10 +860,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.var_debug_info.push(VarDebugInfo { name, source_info: SourceInfo::outermost(tcx_hir.span(var_id)), - place: Place { + value: VarDebugInfoContents::Place(Place { local: closure_env_arg, projection: tcx.intern_place_elems(&projs), - }, + }), }); mutability diff --git a/src/test/codegen/lifetime_start_end.rs b/src/test/codegen/lifetime_start_end.rs index 9df46bb3dd1b5..2296c224f8685 100644 --- a/src/test/codegen/lifetime_start_end.rs +++ b/src/test/codegen/lifetime_start_end.rs @@ -8,7 +8,7 @@ pub fn test() { let a = 0; &a; // keep variable in an alloca -// CHECK: [[S_a:%[0-9]+]] = bitcast i32* %a to i8* +// CHECK: [[S_a:%[0-9]+]] = bitcast i32* %_1 to i8* // CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, i8* [[S_a]]) { @@ -31,12 +31,12 @@ pub fn test() { let c = 1; &c; // keep variable in an alloca -// CHECK: [[S_c:%[0-9]+]] = bitcast i32* %c to i8* +// CHECK: [[S_c:%[0-9]+]] = bitcast i32* %_7 to i8* // CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, i8* [[S_c]]) -// CHECK: [[E_c:%[0-9]+]] = bitcast i32* %c to i8* +// CHECK: [[E_c:%[0-9]+]] = bitcast i32* %_7 to i8* // CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, i8* [[E_c]]) -// CHECK: [[E_a:%[0-9]+]] = bitcast i32* %a to i8* +// CHECK: [[E_a:%[0-9]+]] = bitcast i32* %_1 to i8* // CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, i8* [[E_a]]) } diff --git a/src/test/incremental/hashes/let_expressions.rs b/src/test/incremental/hashes/let_expressions.rs index 918e72582d697..2c37b2e78ffae 100644 --- a/src/test/incremental/hashes/let_expressions.rs +++ b/src/test/incremental/hashes/let_expressions.rs @@ -86,7 +86,7 @@ pub fn change_mutability_of_slot() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_nodes,typeck,optimized_mir")] + except="hir_owner_nodes,typeck")] #[rustc_clean(cfg="cfail3")] pub fn change_mutability_of_slot() { let _x: u64 = 0; @@ -166,7 +166,7 @@ pub fn change_mutability_of_binding_in_pattern() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_nodes,typeck,optimized_mir")] + except="hir_owner_nodes,typeck")] #[rustc_clean(cfg="cfail3")] pub fn change_mutability_of_binding_in_pattern() { let (mut _a, _b) = (99u8, 'q'); diff --git a/src/test/mir-opt/const_debuginfo.main.ConstDebugInfo.diff b/src/test/mir-opt/const_debuginfo.main.ConstDebugInfo.diff new file mode 100644 index 0000000000000..47c3239b8bf25 --- /dev/null +++ b/src/test/mir-opt/const_debuginfo.main.ConstDebugInfo.diff @@ -0,0 +1,115 @@ +- // MIR for `main` before ConstDebugInfo ++ // MIR for `main` after ConstDebugInfo + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/const_debuginfo.rs:8:11: 8:11 + let _1: u8; // in scope 0 at $DIR/const_debuginfo.rs:9:9: 9:10 + let mut _5: u8; // in scope 0 at $DIR/const_debuginfo.rs:12:15: 12:20 + let mut _6: u8; // in scope 0 at $DIR/const_debuginfo.rs:12:15: 12:16 + let mut _7: u8; // in scope 0 at $DIR/const_debuginfo.rs:12:19: 12:20 + let mut _8: u8; // in scope 0 at $DIR/const_debuginfo.rs:12:23: 12:24 + let mut _14: u32; // in scope 0 at $DIR/const_debuginfo.rs:21:13: 21:16 + let mut _15: u32; // in scope 0 at $DIR/const_debuginfo.rs:21:19: 21:22 + scope 1 { +- debug x => _1; // in scope 1 at $DIR/const_debuginfo.rs:9:9: 9:10 ++ debug x => const 1_u8; // in scope 1 at $DIR/const_debuginfo.rs:9:9: 9:10 + let _2: u8; // in scope 1 at $DIR/const_debuginfo.rs:10:9: 10:10 + scope 2 { +- debug y => _2; // in scope 2 at $DIR/const_debuginfo.rs:10:9: 10:10 ++ debug y => const 2_u8; // in scope 2 at $DIR/const_debuginfo.rs:10:9: 10:10 + let _3: u8; // in scope 2 at $DIR/const_debuginfo.rs:11:9: 11:10 + scope 3 { +- debug z => _3; // in scope 3 at $DIR/const_debuginfo.rs:11:9: 11:10 ++ debug z => const 3_u8; // in scope 3 at $DIR/const_debuginfo.rs:11:9: 11:10 + let _4: u8; // in scope 3 at $DIR/const_debuginfo.rs:12:9: 12:12 + scope 4 { +- debug sum => _4; // in scope 4 at $DIR/const_debuginfo.rs:12:9: 12:12 ++ debug sum => const 6_u8; // in scope 4 at $DIR/const_debuginfo.rs:12:9: 12:12 + let _9: &str; // in scope 4 at $DIR/const_debuginfo.rs:14:9: 14:10 + scope 5 { +- debug s => _9; // in scope 5 at $DIR/const_debuginfo.rs:14:9: 14:10 ++ debug s => const "hello, world!"; // in scope 5 at $DIR/const_debuginfo.rs:14:9: 14:10 + let _10: (bool, bool, u32); // in scope 5 at $DIR/const_debuginfo.rs:16:9: 16:10 + scope 6 { + debug f => _10; // in scope 6 at $DIR/const_debuginfo.rs:16:9: 16:10 + let _11: std::option::Option; // in scope 6 at $DIR/const_debuginfo.rs:18:9: 18:10 + scope 7 { + debug o => _11; // in scope 7 at $DIR/const_debuginfo.rs:18:9: 18:10 + let _12: Point; // in scope 7 at $DIR/const_debuginfo.rs:20:9: 20:10 + scope 8 { + debug p => _12; // in scope 8 at $DIR/const_debuginfo.rs:20:9: 20:10 + let _13: u32; // in scope 8 at $DIR/const_debuginfo.rs:21:9: 21:10 + scope 9 { +- debug a => _13; // in scope 9 at $DIR/const_debuginfo.rs:21:9: 21:10 ++ debug a => const 64_u32; // in scope 9 at $DIR/const_debuginfo.rs:21:9: 21:10 + } + } + } + } + } + } + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/const_debuginfo.rs:9:9: 9:10 + _1 = const 1_u8; // scope 0 at $DIR/const_debuginfo.rs:9:13: 9:16 + StorageLive(_2); // scope 1 at $DIR/const_debuginfo.rs:10:9: 10:10 + _2 = const 2_u8; // scope 1 at $DIR/const_debuginfo.rs:10:13: 10:16 + StorageLive(_3); // scope 2 at $DIR/const_debuginfo.rs:11:9: 11:10 + _3 = const 3_u8; // scope 2 at $DIR/const_debuginfo.rs:11:13: 11:16 + StorageLive(_4); // scope 3 at $DIR/const_debuginfo.rs:12:9: 12:12 + StorageLive(_5); // scope 3 at $DIR/const_debuginfo.rs:12:15: 12:20 + StorageLive(_6); // scope 3 at $DIR/const_debuginfo.rs:12:15: 12:16 + _6 = const 1_u8; // scope 3 at $DIR/const_debuginfo.rs:12:15: 12:16 + StorageLive(_7); // scope 3 at $DIR/const_debuginfo.rs:12:19: 12:20 + _7 = const 2_u8; // scope 3 at $DIR/const_debuginfo.rs:12:19: 12:20 + _5 = const 3_u8; // scope 3 at $DIR/const_debuginfo.rs:12:15: 12:20 + StorageDead(_7); // scope 3 at $DIR/const_debuginfo.rs:12:19: 12:20 + StorageDead(_6); // scope 3 at $DIR/const_debuginfo.rs:12:19: 12:20 + StorageLive(_8); // scope 3 at $DIR/const_debuginfo.rs:12:23: 12:24 + _8 = const 3_u8; // scope 3 at $DIR/const_debuginfo.rs:12:23: 12:24 + _4 = const 6_u8; // scope 3 at $DIR/const_debuginfo.rs:12:15: 12:24 + StorageDead(_8); // scope 3 at $DIR/const_debuginfo.rs:12:23: 12:24 + StorageDead(_5); // scope 3 at $DIR/const_debuginfo.rs:12:23: 12:24 + StorageLive(_9); // scope 4 at $DIR/const_debuginfo.rs:14:9: 14:10 + _9 = const "hello, world!"; // scope 4 at $DIR/const_debuginfo.rs:14:13: 14:28 + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [104, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [8191], len: Size { raw: 13 } }, size: Size { raw: 13 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 13 }) + // mir::Constant + // + span: $DIR/const_debuginfo.rs:14:13: 14:28 + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [104, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [8191], len: Size { raw: 13 } }, size: Size { raw: 13 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 13 }) } + StorageLive(_10); // scope 5 at $DIR/const_debuginfo.rs:16:9: 16:10 + (_10.0: bool) = const true; // scope 5 at $DIR/const_debuginfo.rs:16:13: 16:34 + (_10.1: bool) = const false; // scope 5 at $DIR/const_debuginfo.rs:16:13: 16:34 + (_10.2: u32) = const 123_u32; // scope 5 at $DIR/const_debuginfo.rs:16:13: 16:34 + StorageLive(_11); // scope 6 at $DIR/const_debuginfo.rs:18:9: 18:10 + ((_11 as Some).0: u16) = const 99_u16; // scope 6 at $DIR/const_debuginfo.rs:18:13: 18:24 + discriminant(_11) = 1; // scope 6 at $DIR/const_debuginfo.rs:18:13: 18:24 + StorageLive(_12); // scope 7 at $DIR/const_debuginfo.rs:20:9: 20:10 + (_12.0: u32) = const 32_u32; // scope 7 at $DIR/const_debuginfo.rs:20:13: 20:35 + (_12.1: u32) = const 32_u32; // scope 7 at $DIR/const_debuginfo.rs:20:13: 20:35 + StorageLive(_13); // scope 8 at $DIR/const_debuginfo.rs:21:9: 21:10 + StorageLive(_14); // scope 8 at $DIR/const_debuginfo.rs:21:13: 21:16 + _14 = const 32_u32; // scope 8 at $DIR/const_debuginfo.rs:21:13: 21:16 + StorageLive(_15); // scope 8 at $DIR/const_debuginfo.rs:21:19: 21:22 + _15 = const 32_u32; // scope 8 at $DIR/const_debuginfo.rs:21:19: 21:22 + _13 = const 64_u32; // scope 8 at $DIR/const_debuginfo.rs:21:13: 21:22 + StorageDead(_15); // scope 8 at $DIR/const_debuginfo.rs:21:21: 21:22 + StorageDead(_14); // scope 8 at $DIR/const_debuginfo.rs:21:21: 21:22 + _0 = const (); // scope 0 at $DIR/const_debuginfo.rs:8:11: 22:2 + StorageDead(_13); // scope 8 at $DIR/const_debuginfo.rs:22:1: 22:2 + StorageDead(_12); // scope 7 at $DIR/const_debuginfo.rs:22:1: 22:2 + StorageDead(_11); // scope 6 at $DIR/const_debuginfo.rs:22:1: 22:2 + StorageDead(_10); // scope 5 at $DIR/const_debuginfo.rs:22:1: 22:2 + StorageDead(_9); // scope 4 at $DIR/const_debuginfo.rs:22:1: 22:2 + StorageDead(_4); // scope 3 at $DIR/const_debuginfo.rs:22:1: 22:2 + StorageDead(_3); // scope 2 at $DIR/const_debuginfo.rs:22:1: 22:2 + StorageDead(_2); // scope 1 at $DIR/const_debuginfo.rs:22:1: 22:2 + StorageDead(_1); // scope 0 at $DIR/const_debuginfo.rs:22:1: 22:2 + return; // scope 0 at $DIR/const_debuginfo.rs:22:2: 22:2 + } + } + diff --git a/src/test/mir-opt/const_debuginfo.rs b/src/test/mir-opt/const_debuginfo.rs new file mode 100644 index 0000000000000..a66d66c60c7ad --- /dev/null +++ b/src/test/mir-opt/const_debuginfo.rs @@ -0,0 +1,24 @@ +// compile-flags: -C overflow-checks=no + +struct Point { + x: u32, + y: u32, +} + +fn main() { + let x = 1u8; + let y = 2u8; + let z = 3u8; + let sum = x + y + z; + + let s = "hello, world!"; + + let f = (true, false, 123u32); + + let o = Some(99u16); + + let p = Point { x: 32, y: 32 }; + let a = p.x + p.y; +} + +// EMIT_MIR const_debuginfo.main.ConstDebugInfo.diff diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.main.SimplifyLocals.after.32bit.mir b/src/test/mir-opt/const_prop/optimizes_into_variable.main.SimplifyLocals.after.32bit.mir index a78a6341c299d..e4fbba3abfea1 100644 --- a/src/test/mir-opt/const_prop/optimizes_into_variable.main.SimplifyLocals.after.32bit.mir +++ b/src/test/mir-opt/const_prop/optimizes_into_variable.main.SimplifyLocals.after.32bit.mir @@ -2,30 +2,18 @@ fn main() -> () { let mut _0: (); // return place in scope 0 at $DIR/optimizes_into_variable.rs:11:11: 11:11 - let _1: i32; // in scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 scope 1 { - debug x => _1; // in scope 1 at $DIR/optimizes_into_variable.rs:12:9: 12:10 - let _2: i32; // in scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + debug x => const 4_i32; // in scope 1 at $DIR/optimizes_into_variable.rs:12:9: 12:10 scope 2 { - debug y => _2; // in scope 2 at $DIR/optimizes_into_variable.rs:13:9: 13:10 - let _3: u32; // in scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + debug y => const 3_i32; // in scope 2 at $DIR/optimizes_into_variable.rs:13:9: 13:10 scope 3 { - debug z => _3; // in scope 3 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + debug z => const 42_u32; // in scope 3 at $DIR/optimizes_into_variable.rs:14:9: 14:10 } } } bb0: { - StorageLive(_1); // scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 - _1 = const 4_i32; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 - StorageLive(_2); // scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 - _2 = const 3_i32; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 - StorageLive(_3); // scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 - _3 = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38 _0 = const (); // scope 0 at $DIR/optimizes_into_variable.rs:11:11: 15:2 - StorageDead(_3); // scope 2 at $DIR/optimizes_into_variable.rs:15:1: 15:2 - StorageDead(_2); // scope 1 at $DIR/optimizes_into_variable.rs:15:1: 15:2 - StorageDead(_1); // scope 0 at $DIR/optimizes_into_variable.rs:15:1: 15:2 return; // scope 0 at $DIR/optimizes_into_variable.rs:15:2: 15:2 } } diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.main.SimplifyLocals.after.64bit.mir b/src/test/mir-opt/const_prop/optimizes_into_variable.main.SimplifyLocals.after.64bit.mir index a78a6341c299d..e4fbba3abfea1 100644 --- a/src/test/mir-opt/const_prop/optimizes_into_variable.main.SimplifyLocals.after.64bit.mir +++ b/src/test/mir-opt/const_prop/optimizes_into_variable.main.SimplifyLocals.after.64bit.mir @@ -2,30 +2,18 @@ fn main() -> () { let mut _0: (); // return place in scope 0 at $DIR/optimizes_into_variable.rs:11:11: 11:11 - let _1: i32; // in scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 scope 1 { - debug x => _1; // in scope 1 at $DIR/optimizes_into_variable.rs:12:9: 12:10 - let _2: i32; // in scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + debug x => const 4_i32; // in scope 1 at $DIR/optimizes_into_variable.rs:12:9: 12:10 scope 2 { - debug y => _2; // in scope 2 at $DIR/optimizes_into_variable.rs:13:9: 13:10 - let _3: u32; // in scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + debug y => const 3_i32; // in scope 2 at $DIR/optimizes_into_variable.rs:13:9: 13:10 scope 3 { - debug z => _3; // in scope 3 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + debug z => const 42_u32; // in scope 3 at $DIR/optimizes_into_variable.rs:14:9: 14:10 } } } bb0: { - StorageLive(_1); // scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 - _1 = const 4_i32; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 - StorageLive(_2); // scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 - _2 = const 3_i32; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 - StorageLive(_3); // scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 - _3 = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38 _0 = const (); // scope 0 at $DIR/optimizes_into_variable.rs:11:11: 15:2 - StorageDead(_3); // scope 2 at $DIR/optimizes_into_variable.rs:15:1: 15:2 - StorageDead(_2); // scope 1 at $DIR/optimizes_into_variable.rs:15:1: 15:2 - StorageDead(_1); // scope 0 at $DIR/optimizes_into_variable.rs:15:1: 15:2 return; // scope 0 at $DIR/optimizes_into_variable.rs:15:2: 15:2 } } diff --git a/src/test/mir-opt/while_let_loops.change_loop_body.PreCodegen.after.32bit.mir b/src/test/mir-opt/while_let_loops.change_loop_body.PreCodegen.after.32bit.mir index 523ecb5ec1a3d..a75569964a9c8 100644 --- a/src/test/mir-opt/while_let_loops.change_loop_body.PreCodegen.after.32bit.mir +++ b/src/test/mir-opt/while_let_loops.change_loop_body.PreCodegen.after.32bit.mir @@ -2,20 +2,16 @@ fn change_loop_body() -> () { let mut _0: (); // return place in scope 0 at $DIR/while_let_loops.rs:5:27: 5:27 - let mut _1: i32; // in scope 0 at $DIR/while_let_loops.rs:6:9: 6:15 - let mut _2: std::option::Option; // in scope 0 at $DIR/while_let_loops.rs:7:28: 7:32 + let mut _1: std::option::Option; // in scope 0 at $DIR/while_let_loops.rs:7:28: 7:32 scope 1 { - debug _x => _1; // in scope 1 at $DIR/while_let_loops.rs:6:9: 6:15 + debug _x => const 0_i32; // in scope 1 at $DIR/while_let_loops.rs:6:9: 6:15 } bb0: { - StorageLive(_1); // scope 0 at $DIR/while_let_loops.rs:6:9: 6:15 - _1 = const 0_i32; // scope 0 at $DIR/while_let_loops.rs:6:18: 6:19 - StorageLive(_2); // scope 1 at $DIR/while_let_loops.rs:7:28: 7:32 - discriminant(_2) = 0; // scope 1 at $DIR/while_let_loops.rs:7:28: 7:32 + StorageLive(_1); // scope 1 at $DIR/while_let_loops.rs:7:28: 7:32 + discriminant(_1) = 0; // scope 1 at $DIR/while_let_loops.rs:7:28: 7:32 _0 = const (); // scope 1 at $DIR/while_let_loops.rs:7:5: 10:6 - StorageDead(_2); // scope 1 at $DIR/while_let_loops.rs:10:5: 10:6 - StorageDead(_1); // scope 0 at $DIR/while_let_loops.rs:11:1: 11:2 + StorageDead(_1); // scope 1 at $DIR/while_let_loops.rs:10:5: 10:6 return; // scope 0 at $DIR/while_let_loops.rs:11:2: 11:2 } } diff --git a/src/test/mir-opt/while_let_loops.change_loop_body.PreCodegen.after.64bit.mir b/src/test/mir-opt/while_let_loops.change_loop_body.PreCodegen.after.64bit.mir index 523ecb5ec1a3d..a75569964a9c8 100644 --- a/src/test/mir-opt/while_let_loops.change_loop_body.PreCodegen.after.64bit.mir +++ b/src/test/mir-opt/while_let_loops.change_loop_body.PreCodegen.after.64bit.mir @@ -2,20 +2,16 @@ fn change_loop_body() -> () { let mut _0: (); // return place in scope 0 at $DIR/while_let_loops.rs:5:27: 5:27 - let mut _1: i32; // in scope 0 at $DIR/while_let_loops.rs:6:9: 6:15 - let mut _2: std::option::Option; // in scope 0 at $DIR/while_let_loops.rs:7:28: 7:32 + let mut _1: std::option::Option; // in scope 0 at $DIR/while_let_loops.rs:7:28: 7:32 scope 1 { - debug _x => _1; // in scope 1 at $DIR/while_let_loops.rs:6:9: 6:15 + debug _x => const 0_i32; // in scope 1 at $DIR/while_let_loops.rs:6:9: 6:15 } bb0: { - StorageLive(_1); // scope 0 at $DIR/while_let_loops.rs:6:9: 6:15 - _1 = const 0_i32; // scope 0 at $DIR/while_let_loops.rs:6:18: 6:19 - StorageLive(_2); // scope 1 at $DIR/while_let_loops.rs:7:28: 7:32 - discriminant(_2) = 0; // scope 1 at $DIR/while_let_loops.rs:7:28: 7:32 + StorageLive(_1); // scope 1 at $DIR/while_let_loops.rs:7:28: 7:32 + discriminant(_1) = 0; // scope 1 at $DIR/while_let_loops.rs:7:28: 7:32 _0 = const (); // scope 1 at $DIR/while_let_loops.rs:7:5: 10:6 - StorageDead(_2); // scope 1 at $DIR/while_let_loops.rs:10:5: 10:6 - StorageDead(_1); // scope 0 at $DIR/while_let_loops.rs:11:1: 11:2 + StorageDead(_1); // scope 1 at $DIR/while_let_loops.rs:10:5: 10:6 return; // scope 0 at $DIR/while_let_loops.rs:11:2: 11:2 } } From 22026538931f7349a1b78d7644b0288ff3282db2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 21 Oct 2020 09:26:11 +0200 Subject: [PATCH 27/33] Miri engine validity check: simplify code with 'matches!' and improve a comment a bit --- compiler/rustc_mir/src/interpret/validity.rs | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_mir/src/interpret/validity.rs b/compiler/rustc_mir/src/interpret/validity.rs index 2b83e1c8134ef..c38f25564e8dd 100644 --- a/compiler/rustc_mir/src/interpret/validity.rs +++ b/compiler/rustc_mir/src/interpret/validity.rs @@ -775,17 +775,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> ); } ty::Array(tys, ..) | ty::Slice(tys) - if { - // This optimization applies for types that can hold arbitrary bytes (such as - // integer and floating point types) or for structs or tuples with no fields. - // FIXME(wesleywiser) This logic could be extended further to arbitrary structs - // or tuples made up of integer/floating point types or inhabited ZSTs with no - // padding. - match tys.kind() { - ty::Int(..) | ty::Uint(..) | ty::Float(..) => true, - _ => false, - } - } => + // This optimization applies for types that can hold arbitrary bytes (such as + // integer and floating point types) or for structs or tuples with no fields. + // FIXME(wesleywiser) This logic could be extended further to arbitrary structs + // or tuples made up of integer/floating point types or inhabited ZSTs with no + // padding. + if matches!(tys.kind(), ty::Int(..) | ty::Uint(..) | ty::Float(..)) + => { // Optimized handling for arrays of integer/float type. @@ -853,7 +849,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> // of an array and not all of them, because there's only a single value of a specific // ZST type, so either validation fails for all elements or none. ty::Array(tys, ..) | ty::Slice(tys) if self.ecx.layout_of(tys)?.is_zst() => { - // Validate just the first element + // Validate just the first element (if any). self.walk_aggregate(op, fields.take(1))? } _ => { From fcaf2338da4456f2b3d1ccf3e127f9028c80a2cc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 21 Oct 2020 09:47:15 +0200 Subject: [PATCH 28/33] Miri engine interning: improve comments, and entirely skip ZST --- compiler/rustc_mir/src/interpret/intern.rs | 28 +++++++++++++--------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_mir/src/interpret/intern.rs b/compiler/rustc_mir/src/interpret/intern.rs index 945791eddc8f1..846ca18990022 100644 --- a/compiler/rustc_mir/src/interpret/intern.rs +++ b/compiler/rustc_mir/src/interpret/intern.rs @@ -33,8 +33,9 @@ struct InternVisitor<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>> { /// A list of all encountered allocations. After type-based interning, we traverse this list to /// also intern allocations that are only referenced by a raw pointer or inside a union. leftover_allocations: &'rt mut FxHashSet, - /// The root kind of the value that we're looking at. This field is never mutated and only used - /// for sanity assertions that will ICE when `const_qualif` screws up. + /// The root kind of the value that we're looking at. This field is never mutated for a + /// particular allocation. It is primarily used to make as many allocations as possible + /// read-only so LLVM can place them in const memory. mode: InternMode, /// This field stores whether we are *currently* inside an `UnsafeCell`. This can affect /// the intern mode of references we encounter. @@ -113,8 +114,8 @@ fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>>( // For this, we need to take into account `UnsafeCell`. When `ty` is `None`, we assume // no interior mutability. let frozen = ty.map_or(true, |ty| ty.is_freeze(ecx.tcx, ecx.param_env)); - // For statics, allocation mutability is the combination of the place mutability and - // the type mutability. + // For statics, allocation mutability is the combination of place mutability and + // type mutability. // The entire allocation needs to be mutable if it contains an `UnsafeCell` anywhere. let immutable = mutability == Mutability::Not && frozen; if immutable { @@ -188,8 +189,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir } } - // ZSTs do not need validation unless they're uninhabited - if mplace.layout.is_zst() && !mplace.layout.abi.is_uninhabited() { + // ZSTs cannot contain pointers, so we can skip them. + if mplace.layout.is_zst() { return Ok(()); } @@ -209,13 +210,12 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir if let ty::Dynamic(..) = tcx.struct_tail_erasing_lifetimes(referenced_ty, self.ecx.param_env).kind() { - // Validation will error (with a better message) on an invalid vtable pointer - // so we can safely not do anything if this is not a real pointer. if let Scalar::Ptr(vtable) = mplace.meta.unwrap_meta() { // Explicitly choose const mode here, since vtables are immutable, even // if the reference of the fat pointer is mutable. self.intern_shallow(vtable.alloc_id, InternMode::ConstInner, None); } else { + // Validation will error (with a better message) on an invalid vtable pointer. // Let validation show the error message, but make sure it *does* error. tcx.sess .delay_span_bug(tcx.span, "vtables pointers cannot be integer pointers"); @@ -224,7 +224,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir // Check if we have encountered this pointer+layout combination before. // Only recurse for allocation-backed pointers. if let Scalar::Ptr(ptr) = mplace.ptr { - // Compute the mode with which we intern this. + // Compute the mode with which we intern this. Our goal here is to make as many + // statics as we can immutable so they can be placed in const memory by LLVM. let ref_mode = match self.mode { InternMode::Static(mutbl) => { // In statics, merge outer mutability with reference mutability and @@ -243,8 +244,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir } Mutability::Not => { // A shared reference, things become immutable. - // We do *not* consier `freeze` here -- that is done more precisely - // when traversing the referenced data (by tracking `UnsafeCell`). + // We do *not* consider `freeze` here: `intern_shallow` considers + // `freeze` for the actual mutability of this allocation; the intern + // mode for references contained in this allocation is tracked more + // precisely when traversing the referenced data (by tracking + // `UnsafeCell`). This makes sure that `&(&i32, &Cell)` still + // has the left inner reference interned into a read-only + // allocation. InternMode::Static(Mutability::Not) } Mutability::Mut => { From 35194117006d987d59dabb44ee3e4f38281c9e1b Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 21 Oct 2020 19:24:59 +0100 Subject: [PATCH 29/33] Add a test for #53708 This issue was accidentally fixed recently, probably by #70743 --- src/test/ui/consts/const_in_pattern/issue-53708.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/test/ui/consts/const_in_pattern/issue-53708.rs diff --git a/src/test/ui/consts/const_in_pattern/issue-53708.rs b/src/test/ui/consts/const_in_pattern/issue-53708.rs new file mode 100644 index 0000000000000..355ba63790f3b --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/issue-53708.rs @@ -0,0 +1,11 @@ +// check-pass +// https://github.com/rust-lang/rust/issues/53708 +#[derive(PartialEq, Eq)] +struct S; + +fn main() { + const C: &S = &S; + match C { + C => {} + } +} From faf87105db54a399bc598765528dec818b63a54a Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 21 Oct 2020 20:15:02 +0100 Subject: [PATCH 30/33] Explain the `Opaque` special case in specialization --- .../src/thir/pattern/_match.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index a7ddca1b6b632..4c69339795d40 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -2477,7 +2477,24 @@ fn specialize_one_pattern<'p, 'tcx>( if let Opaque = constructor { // Only a wildcard pattern can match an opaque constant, unless we're specializing the - // value against its own constructor. + // value against its own constructor. That happens when we call + // `v.specialize_constructor(ctor)` with `ctor` obtained from `pat_constructor(v.head())`. + // For example, in the following match, when we are dealing with the third branch, we will + // specialize with an `Opaque` ctor. We want to ignore the second branch because opaque + // constants should not be inspected, but we don't want to ignore the current (third) + // branch, as that would cause us to always conclude that such a branch is unreachable. + // ```rust + // #[derive(PartialEq)] + // struct Foo(i32); + // impl Eq for Foo {} + // const FOO: Foo = Foo(42); + // + // match (Foo(0), true) { + // (_, true) => {} + // (FOO, true) => {} + // (FOO, false) => {} + // } + // ``` if is_its_own_ctor || pat.is_wildcard() { return Some(Fields::empty()); } else { From 484d9ebc293ae5e6011e7b7ee99a887d47f3f590 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Thu, 22 Oct 2020 00:04:25 +0200 Subject: [PATCH 31/33] Move fmt-write-bloat test to run-make-fulldeps. --- .../{run-make => run-make-fulldeps}/fmt-write-bloat/Makefile | 2 +- .../{run-make => run-make-fulldeps}/fmt-write-bloat/main.rs | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/test/{run-make => run-make-fulldeps}/fmt-write-bloat/Makefile (78%) rename src/test/{run-make => run-make-fulldeps}/fmt-write-bloat/main.rs (100%) diff --git a/src/test/run-make/fmt-write-bloat/Makefile b/src/test/run-make-fulldeps/fmt-write-bloat/Makefile similarity index 78% rename from src/test/run-make/fmt-write-bloat/Makefile rename to src/test/run-make-fulldeps/fmt-write-bloat/Makefile index 4227adb0d307c..811ec4b31934d 100644 --- a/src/test/run-make/fmt-write-bloat/Makefile +++ b/src/test/run-make-fulldeps/fmt-write-bloat/Makefile @@ -1,4 +1,4 @@ --include ../../run-make-fulldeps/tools.mk +-include ../tools.mk NM=nm diff --git a/src/test/run-make/fmt-write-bloat/main.rs b/src/test/run-make-fulldeps/fmt-write-bloat/main.rs similarity index 100% rename from src/test/run-make/fmt-write-bloat/main.rs rename to src/test/run-make-fulldeps/fmt-write-bloat/main.rs From 3a58ad91f68c0715301b9f0272954a18b72eb6bb Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 22 Oct 2020 07:10:25 +0900 Subject: [PATCH 32/33] Update `compiler_builtins` to 0.1.36 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0d2170a992747..b70e3e89b1a94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -626,9 +626,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.35" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3fcd8aba10d17504c87ef12d4f62ef404c6a4703d16682a9eb5543e6cf24455" +checksum = "7cd0782e0a7da7598164153173e5a5d4d9b1da094473c98dce0ff91406112369" dependencies = [ "cc", "rustc-std-workspace-core", From 10d6f2d7ea55ec1a8bafcac23e731ac69e470f5b Mon Sep 17 00:00:00 2001 From: varkor Date: Wed, 21 Oct 2020 23:01:50 +0100 Subject: [PATCH 33/33] Add regression test for #73298 --- src/test/ui/const-generics/issue-73298.rs | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/test/ui/const-generics/issue-73298.rs diff --git a/src/test/ui/const-generics/issue-73298.rs b/src/test/ui/const-generics/issue-73298.rs new file mode 100644 index 0000000000000..4ad4108c90581 --- /dev/null +++ b/src/test/ui/const-generics/issue-73298.rs @@ -0,0 +1,24 @@ +// build-pass +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] + +use std::convert::AsMut; +use std::default::Default; + +trait Foo: Sized { + type Baz: Default + AsMut<[u8]>; + fn bar() { + Self::Baz::default().as_mut(); + } +} + +impl Foo for () { + type Baz = [u8; 1 * 1]; +} + +fn main() { + <() as Foo>::bar(); +}