]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #62564 - Mark-Simulacrum:azure-line-endings, r=pietroalbini
authorbors <bors@rust-lang.org>
Wed, 10 Jul 2019 19:36:42 +0000 (19:36 +0000)
committerbors <bors@rust-lang.org>
Wed, 10 Jul 2019 19:36:42 +0000 (19:36 +0000)
Ensure that checkout is with \n line endings

During installation of mingw, at least, the git directories change, so
we need to reset the core.autocrlf config to false.

Once we finish checking out submodules, check that the line endings are
\n and not \r\n.

Artifacts were built via the last try on #62545; I've manually confirmed that `install.sh` appears to no longer have `\r\n` line endings.

Fixes #62276.

39 files changed:
src/librustc/lint/builtin.rs
src/librustc/middle/lang_items.rs
src/librustc/mir/interpret/value.rs
src/librustc_lint/lib.rs
src/librustc_mir/const_eval.rs
src/librustc_mir/hair/pattern/check_match.rs
src/librustc_mir/hair/pattern/mod.rs
src/librustc_mir/interpret/memory.rs
src/librustc_mir/interpret/operand.rs
src/librustc_mir/interpret/place.rs
src/librustc_mir/interpret/step.rs
src/librustc_mir/interpret/terminator.rs
src/librustc_mir/interpret/validity.rs
src/librustc_mir/interpret/visitor.rs
src/test/ui/issues/issue-55511.rs
src/test/ui/issues/issue-55511.stderr
src/test/ui/rfc1445/allow-hide-behind-direct-unsafe-ptr-embedded.rs [new file with mode: 0644]
src/test/ui/rfc1445/allow-hide-behind-direct-unsafe-ptr-param.rs [new file with mode: 0644]
src/test/ui/rfc1445/allow-hide-behind-indirect-unsafe-ptr-embedded.rs [new file with mode: 0644]
src/test/ui/rfc1445/allow-hide-behind-indirect-unsafe-ptr-param.rs [new file with mode: 0644]
src/test/ui/rfc1445/cant-hide-behind-direct-struct-embedded.rs [new file with mode: 0644]
src/test/ui/rfc1445/cant-hide-behind-direct-struct-embedded.stderr [new file with mode: 0644]
src/test/ui/rfc1445/cant-hide-behind-direct-struct-param.rs [new file with mode: 0644]
src/test/ui/rfc1445/cant-hide-behind-direct-struct-param.stderr [new file with mode: 0644]
src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-embedded.rs [new file with mode: 0644]
src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-embedded.stderr [new file with mode: 0644]
src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-param.rs [new file with mode: 0644]
src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-param.stderr [new file with mode: 0644]
src/test/ui/rfc1445/cant-hide-behind-indirect-struct-embedded.rs [new file with mode: 0644]
src/test/ui/rfc1445/cant-hide-behind-indirect-struct-embedded.stderr [new file with mode: 0644]
src/test/ui/rfc1445/cant-hide-behind-indirect-struct-param.rs [new file with mode: 0644]
src/test/ui/rfc1445/cant-hide-behind-indirect-struct-param.stderr [new file with mode: 0644]
src/test/ui/rfc1445/issue-61118-match-slice-forbidden-without-eq.rs [new file with mode: 0644]
src/test/ui/rfc1445/issue-61118-match-slice-forbidden-without-eq.stderr [new file with mode: 0644]
src/test/ui/rfc1445/issue-62307-match-ref-ref-forbidden-without-eq.rs [new file with mode: 0644]
src/test/ui/rfc1445/issue-62307-match-ref-ref-forbidden-without-eq.stderr [new file with mode: 0644]
src/test/ui/rfc1445/match-empty-array-allowed-without-eq-issue-62336.rs [new file with mode: 0644]
src/test/ui/rfc1445/match-nonempty-array-forbidden-without-eq.rs [new file with mode: 0644]
src/test/ui/rfc1445/match-nonempty-array-forbidden-without-eq.stderr [new file with mode: 0644]

index dd879ec6aff34f773016aad1c3de6fe2a449ca56..9b4b97202d96de04a9b147c5e62c52b2b51ab43c 100644 (file)
     "outlives requirements can be inferred"
 }
 
+declare_lint! {
+    pub INDIRECT_STRUCTURAL_MATCH,
+    Warn,
+    "pattern with const indirectly referencing non-`#[structural_match]` type"
+}
+
 /// Some lints that are buffered from `libsyntax`. See `syntax::early_buffered_lints`.
 pub mod parser {
     declare_lint! {
index 7c15f2ef94d1b4e259d106583e83392482239f5c..bdd48b34474981d9841faa1769d6749992f841a3 100644 (file)
@@ -326,6 +326,7 @@ pub fn collect<'tcx>(tcx: TyCtxt<'tcx>) -> LanguageItems {
     UnpinTraitLangItem,          "unpin",              unpin_trait,             Target::Trait;
     PinTypeLangItem,             "pin",                pin_type,                Target::Struct;
 
+    // Don't be fooled by the naming here: this lang item denotes `PartialEq`, not `Eq`.
     EqTraitLangItem,             "eq",                 eq_trait,                Target::Trait;
     PartialOrdTraitLangItem,     "partial_ord",        partial_ord_trait,       Target::Trait;
     OrdTraitLangItem,            "ord",                ord_trait,               Target::Trait;
index 867565d5e0922bec9359ea8d1369dfcb7ca35abb..4a59d845b3b42eebf76cf722e7e642d200126f68 100644 (file)
@@ -262,19 +262,6 @@ pub fn ptr_wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self
         }
     }
 
-    /// Returns this pointer's offset from the allocation base, or from NULL (for
-    /// integer pointers).
-    #[inline]
-    pub fn get_ptr_offset(self, cx: &impl HasDataLayout) -> Size {
-        match self {
-            Scalar::Raw { data, size } => {
-                assert_eq!(size as u64, cx.pointer_size().bytes());
-                Size::from_bytes(data as u64)
-            }
-            Scalar::Ptr(ptr) => ptr.offset,
-        }
-    }
-
     #[inline]
     pub fn from_bool(b: bool) -> Self {
         Scalar::Raw { data: b as u128, size: 1 }
@@ -339,6 +326,10 @@ pub fn from_f64(f: Double) -> Self {
         Scalar::Raw { data: f.to_bits(), size: 8 }
     }
 
+    /// This is very rarely the method you want!  You should dispatch on the type
+    /// and use `force_bits`/`assert_bits`/`force_ptr`/`assert_ptr`.
+    /// This method only exists for the benefit of low-level memory operations
+    /// as well as the implementation of the `force_*` methods.
     #[inline]
     pub fn to_bits_or_ptr(
         self,
@@ -359,6 +350,7 @@ pub fn to_bits_or_ptr(
         }
     }
 
+    /// Do not call this method!  Use either `assert_bits` or `force_bits`.
     #[inline]
     pub fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> {
         match self {
@@ -372,6 +364,12 @@ pub fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> {
         }
     }
 
+    #[inline(always)]
+    pub fn assert_bits(self, target_size: Size) -> u128 {
+        self.to_bits(target_size).expect("Expected Raw bits but got a Pointer")
+    }
+
+    /// Do not call this method!  Use either `assert_ptr` or `force_ptr`.
     #[inline]
     pub fn to_ptr(self) -> InterpResult<'tcx, Pointer<Tag>> {
         match self {
@@ -381,6 +379,12 @@ pub fn to_ptr(self) -> InterpResult<'tcx, Pointer<Tag>> {
         }
     }
 
+    #[inline(always)]
+    pub fn assert_ptr(self) -> Pointer<Tag> {
+        self.to_ptr().expect("Expected a Pointer but got Raw bits")
+    }
+
+    /// Do not call this method!  Dispatch based on the type instead.
     #[inline]
     pub fn is_bits(self) -> bool {
         match self {
@@ -389,6 +393,7 @@ pub fn is_bits(self) -> bool {
         }
     }
 
+    /// Do not call this method!  Dispatch based on the type instead.
     #[inline]
     pub fn is_ptr(self) -> bool {
         match self {
@@ -536,11 +541,13 @@ pub fn not_undef(self) -> InterpResult<'static, Scalar<Tag>> {
         }
     }
 
+    /// Do not call this method!  Use either `assert_ptr` or `force_ptr`.
     #[inline(always)]
     pub fn to_ptr(self) -> InterpResult<'tcx, Pointer<Tag>> {
         self.not_undef()?.to_ptr()
     }
 
+    /// Do not call this method!  Use either `assert_bits` or `force_bits`.
     #[inline(always)]
     pub fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> {
         self.not_undef()?.to_bits(target_size)
index 13a9963fa901aaae1518dee6ae78446256587d72..82348e330f5b6d31a2facc1dc5901f48132b7ddd 100644 (file)
@@ -429,6 +429,11 @@ macro_rules! register_passes {
             id: LintId::of(MUTABLE_BORROW_RESERVATION_CONFLICT),
             reference: "issue #59159 <https://github.com/rust-lang/rust/issues/59159>",
             edition: None,
+        },
+        FutureIncompatibleInfo {
+            id: LintId::of(INDIRECT_STRUCTURAL_MATCH),
+            reference: "issue #62411 <https://github.com/rust-lang/rust/issues/62411>",
+            edition: None,
         }
         ]);
 
index f8de1cfaea0980361e6b770489c59a2a02e1a22d..8f3364b1fba19b7420cc50e920bb504e2fb4213c 100644 (file)
@@ -109,7 +109,7 @@ fn op_to_const<'tcx>(
                 // `Immediate` is when we are called from `const_field`, and that `Immediate`
                 // comes from a constant so it can happen have `Undef`, because the indirect
                 // memory that was read had undefined bytes.
-                let mplace = op.to_mem_place();
+                let mplace = op.assert_mem_place();
                 let ptr = mplace.ptr.to_ptr().unwrap();
                 let alloc = ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id);
                 ConstValue::ByRef { offset: ptr.offset, align: mplace.align, alloc }
@@ -661,7 +661,7 @@ pub fn const_eval_raw_provider<'tcx>(
         |body| eval_body_using_ecx(&mut ecx, cid, body, key.param_env)
     ).and_then(|place| {
         Ok(RawConst {
-            alloc_id: place.to_ptr().expect("we allocated this ptr!").alloc_id,
+            alloc_id: place.ptr.assert_ptr().alloc_id,
             ty: place.layout.ty
         })
     }).map_err(|error| {
index 42bbe88283b78d9520dc8568fcec445c165e0a62..32a8c5cd3bb28f92c35c613662c020e469715344 100644 (file)
@@ -170,6 +170,7 @@ fn check_match(
                     let mut patcx = PatternContext::new(self.tcx,
                                                         self.param_env.and(self.identity_substs),
                                                         self.tables);
+                    patcx.include_lint_checks();
                     let pattern = expand_pattern(cx, patcx.lower_pattern(&pat));
                     if !patcx.errors.is_empty() {
                         patcx.report_inlining_errors(pat.span);
@@ -266,6 +267,7 @@ fn check_irrefutable(&self, pat: &'tcx Pat, origin: &str) {
             let mut patcx = PatternContext::new(self.tcx,
                                                 self.param_env.and(self.identity_substs),
                                                 self.tables);
+            patcx.include_lint_checks();
             let pattern = patcx.lower_pattern(pat);
             let pattern_ty = pattern.ty;
             let pats: Matrix<'_, '_> = vec![smallvec![
index 6ba2f587768497bf9b89e29e32569ee47227dbcb..1baeda41498ed0d50392d679e6dc886d501ef1a0 100644 (file)
 use crate::hair::util::UserAnnotatedTyHelpers;
 use crate::hair::constant::*;
 
+use rustc::lint;
 use rustc::mir::{Field, BorrowKind, Mutability};
 use rustc::mir::{UserTypeProjection};
 use rustc::mir::interpret::{GlobalId, ConstValue, sign_extend, AllocId, Pointer};
+use rustc::traits::{ObligationCause, PredicateObligation};
 use rustc::ty::{self, Region, TyCtxt, AdtDef, Ty, UserType, DefIdTree};
 use rustc::ty::{CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations};
 use rustc::ty::subst::{SubstsRef, Kind};
@@ -23,6 +25,7 @@
 use rustc::hir::ptr::P;
 
 use rustc_data_structures::indexed_vec::Idx;
+use rustc_data_structures::fx::FxHashSet;
 
 use std::cmp::Ordering;
 use std::fmt;
@@ -332,6 +335,7 @@ pub struct PatternContext<'a, 'tcx> {
     pub tables: &'a ty::TypeckTables<'tcx>,
     pub substs: SubstsRef<'tcx>,
     pub errors: Vec<PatternError>,
+    include_lint_checks: bool,
 }
 
 impl<'a, 'tcx> Pattern<'tcx> {
@@ -363,10 +367,16 @@ pub fn new(
             param_env: param_env_and_substs.param_env,
             tables,
             substs: param_env_and_substs.value,
-            errors: vec![]
+            errors: vec![],
+            include_lint_checks: false,
         }
     }
 
+    pub fn include_lint_checks(&mut self) -> &mut Self {
+        self.include_lint_checks = true;
+        self
+    }
+
     pub fn lower_pattern(&mut self, pat: &'tcx hir::Pat) -> Pattern<'tcx> {
         // When implicit dereferences have been inserted in this pattern, the unadjusted lowered
         // pattern has the type that results *after* dereferencing. For example, in this code:
@@ -942,7 +952,7 @@ fn lower_lit(&mut self, expr: &'tcx hir::Expr) -> PatternKind<'tcx> {
 
     /// Converts an evaluated constant to a pattern (if possible).
     /// This means aggregate values (like structs and enums) are converted
-    /// to a pattern that matches the value (as if you'd compared via equality).
+    /// to a pattern that matches the value (as if you'd compared via structural equality).
     fn const_to_pat(
         &self,
         instance: ty::Instance<'tcx>,
@@ -950,15 +960,86 @@ fn const_to_pat(
         id: hir::HirId,
         span: Span,
     ) -> Pattern<'tcx> {
+        // This method is just a warpper handling a validity check; the heavy lifting is
+        // performed by the recursive const_to_pat_inner method, which is not meant to be
+        // invoked except by this method.
+        //
+        // once indirect_structural_match is a full fledged error, this
+        // level of indirection can be eliminated
+
         debug!("const_to_pat: cv={:#?} id={:?}", cv, id);
-        let adt_subpattern = |i, variant_opt| {
+        debug!("const_to_pat: cv.ty={:?} span={:?}", cv.ty, span);
+
+        let mut saw_error = false;
+        let inlined_const_as_pat = self.const_to_pat_inner(instance, cv, id, span, &mut saw_error);
+
+        if self.include_lint_checks && !saw_error {
+            // If we were able to successfully convert the const to some pat, double-check
+            // that the type of the const obeys `#[structural_match]` constraint.
+            if let Some(adt_def) = search_for_adt_without_structural_match(self.tcx, cv.ty) {
+
+                let path = self.tcx.def_path_str(adt_def.did);
+                let msg = format!(
+                    "to use a constant of type `{}` in a pattern, \
+                     `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
+                    path,
+                    path,
+                );
+
+                // before issuing lint, double-check there even *is* a
+                // semantic PartialEq for us to dispatch to.
+                //
+                // (If there isn't, then we can safely issue a hard
+                // error, because that's never worked, due to compiler
+                // using PartialEq::eq in this scenario in the past.)
+
+                let ty_is_partial_eq: bool = {
+                    let partial_eq_trait_id = self.tcx.lang_items().eq_trait().unwrap();
+                    let obligation: PredicateObligation<'_> =
+                        self.tcx.predicate_for_trait_def(self.param_env,
+                                                         ObligationCause::misc(span, id),
+                                                         partial_eq_trait_id,
+                                                         0,
+                                                         cv.ty,
+                                                         &[]);
+                    self.tcx
+                        .infer_ctxt()
+                        .enter(|infcx| infcx.predicate_may_hold(&obligation))
+                };
+
+                if !ty_is_partial_eq {
+                    // span_fatal avoids ICE from resolution of non-existent method (rare case).
+                    self.tcx.sess.span_fatal(span, &msg);
+                } else {
+                    self.tcx.lint_hir(lint::builtin::INDIRECT_STRUCTURAL_MATCH, id, span, &msg);
+                }
+            }
+        }
+
+        inlined_const_as_pat
+    }
+
+    /// Recursive helper for `const_to_pat`; invoke that (instead of calling this directly).
+    fn const_to_pat_inner(
+        &self,
+        instance: ty::Instance<'tcx>,
+        cv: &'tcx ty::Const<'tcx>,
+        id: hir::HirId,
+        span: Span,
+        // This tracks if we signal some hard error for a given const
+        // value, so that we will not subsequently issue an irrelevant
+        // lint for the same const value.
+        saw_const_match_error: &mut bool,
+    ) -> Pattern<'tcx> {
+
+        let mut adt_subpattern = |i, variant_opt| {
             let field = Field::new(i);
             let val = crate::const_eval::const_field(
                 self.tcx, self.param_env, variant_opt, field, cv
             );
-            self.const_to_pat(instance, val, id, span)
+            self.const_to_pat_inner(instance, val, id, span, saw_const_match_error)
         };
-        let adt_subpatterns = |n, variant_opt| {
+        let mut adt_subpatterns = |n, variant_opt| {
             (0..n).map(|i| {
                 let field = Field::new(i);
                 FieldPattern {
@@ -967,7 +1048,8 @@ fn const_to_pat(
                 }
             }).collect::<Vec<_>>()
         };
-        debug!("const_to_pat: cv.ty={:?} span={:?}", cv.ty, span);
+
+
         let kind = match cv.ty.sty {
             ty::Float(_) => {
                 self.tcx.lint_hir(
@@ -982,9 +1064,11 @@ fn const_to_pat(
             }
             ty::Adt(adt_def, _) if adt_def.is_union() => {
                 // Matching on union fields is unsafe, we can't hide it in constants
+                *saw_const_match_error = true;
                 self.tcx.sess.span_err(span, "cannot use unions in constant patterns");
                 PatternKind::Wild
             }
+            // keep old code until future-compat upgraded to errors.
             ty::Adt(adt_def, _) if !self.tcx.has_attr(adt_def.did, sym::structural_match) => {
                 let path = self.tcx.def_path_str(adt_def.did);
                 let msg = format!(
@@ -993,9 +1077,11 @@ fn const_to_pat(
                     path,
                     path,
                 );
+                *saw_const_match_error = true;
                 self.tcx.sess.span_err(span, &msg);
                 PatternKind::Wild
             }
+            // keep old code until future-compat upgraded to errors.
             ty::Ref(_, ty::TyS { sty: ty::Adt(adt_def, _), .. }, _)
             if !self.tcx.has_attr(adt_def.did, sym::structural_match) => {
                 // HACK(estebank): Side-step ICE #53708, but anything other than erroring here
@@ -1007,6 +1093,7 @@ fn const_to_pat(
                     path,
                     path,
                 );
+                *saw_const_match_error = true;
                 self.tcx.sess.span_err(span, &msg);
                 PatternKind::Wild
             }
@@ -1058,6 +1145,120 @@ fn const_to_pat(
     }
 }
 
+/// This method traverses the structure of `ty`, trying to find an
+/// instance of an ADT (i.e. struct or enum) that was declared without
+/// the `#[structural_match]` attribute.
+///
+/// The "structure of a type" includes all components that would be
+/// considered when doing a pattern match on a constant of that
+/// type.
+///
+///  * This means this method descends into fields of structs/enums,
+///    and also descends into the inner type `T` of `&T` and `&mut T`
+///
+///  * The traversal doesn't dereference unsafe pointers (`*const T`,
+///    `*mut T`), and it does not visit the type arguments of an
+///    instantiated generic like `PhantomData<T>`.
+///
+/// The reason we do this search is Rust currently require all ADT's
+/// reachable from a constant's type to be annotated with
+/// `#[structural_match]`, an attribute which essentially says that
+/// the implementation of `PartialEq::eq` behaves *equivalently* to a
+/// comparison against the unfolded structure.
+///
+/// For more background on why Rust has this requirement, and issues
+/// that arose when the requirement was not enforced completely, see
+/// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307.
+fn search_for_adt_without_structural_match<'tcx>(tcx: TyCtxt<'tcx>,
+                                                 ty: Ty<'tcx>)
+                                                 -> Option<&'tcx AdtDef>
+{
+    // Import here (not mod level), because `TypeFoldable::fold_with`
+    // conflicts with `PatternFoldable::fold_with`
+    use crate::rustc::ty::fold::TypeVisitor;
+    use crate::rustc::ty::TypeFoldable;
+
+    let mut search = Search { tcx, found: None, seen: FxHashSet::default() };
+    ty.visit_with(&mut search);
+    return search.found;
+
+    struct Search<'tcx> {
+        tcx: TyCtxt<'tcx>,
+
+        // records the first ADT we find without `#[structural_match`
+        found: Option<&'tcx AdtDef>,
+
+        // tracks ADT's previously encountered during search, so that
+        // we will not recur on them again.
+        seen: FxHashSet<&'tcx AdtDef>,
+    }
+
+    impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> {
+        fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
+            debug!("Search visiting ty: {:?}", ty);
+
+            let (adt_def, substs) = match ty.sty {
+                ty::Adt(adt_def, substs) => (adt_def, substs),
+                ty::RawPtr(..) => {
+                    // `#[structural_match]` ignores substructure of
+                    // `*const _`/`*mut _`, so skip super_visit_with
+
+                    // (But still tell caller to continue search.)
+                    return false;
+                }
+                ty::Array(_, n) if n.assert_usize(self.tcx) == Some(0) => {
+                    // rust-lang/rust#62336: ignore type of contents
+                    // for empty array.
+                    return false;
+                }
+                _ => {
+                    ty.super_visit_with(self);
+                    return false;
+                }
+            };
+
+            if !self.tcx.has_attr(adt_def.did, sym::structural_match) {
+                self.found = Some(&adt_def);
+                debug!("Search found adt_def: {:?}", adt_def);
+                return true // Halt visiting!
+            }
+
+            if self.seen.contains(adt_def) {
+                debug!("Search already seen adt_def: {:?}", adt_def);
+                // let caller continue its search
+                return false;
+            }
+
+            self.seen.insert(adt_def);
+
+            // `#[structural_match]` does not care about the
+            // instantiation of the generics in an ADT (it
+            // instead looks directly at its fields outside
+            // this match), so we skip super_visit_with.
+            //
+            // (Must not recur on substs for `PhantomData<T>` cf
+            // rust-lang/rust#55028 and rust-lang/rust#55837; but also
+            // want to skip substs when only uses of generic are
+            // behind unsafe pointers `*const T`/`*mut T`.)
+
+            // even though we skip super_visit_with, we must recur on
+            // fields of ADT.
+            let tcx = self.tcx;
+            for field_ty in adt_def.all_fields().map(|field| field.ty(tcx, substs)) {
+                if field_ty.visit_with(self) {
+                    // found an ADT without `#[structural_match]`; halt visiting!
+                    assert!(self.found.is_some());
+                    return true;
+                }
+            }
+
+            // Even though we do not want to recur on substs, we do
+            // want our caller to continue its own search.
+            false
+        }
+    }
+}
+
 impl UserAnnotatedTyHelpers<'tcx> for PatternContext<'_, 'tcx> {
     fn tcx(&self) -> TyCtxt<'tcx> {
         self.tcx
index 33cd7330069e394ebd2801ed18b2c54f4ab594f2..3f2a76a77be36b8f6f847482de41871418849830 100644 (file)
@@ -214,10 +214,8 @@ pub fn reallocate(
             None => Size::from_bytes(self.get(ptr.alloc_id)?.bytes.len() as u64),
         };
         self.copy(
-            ptr.into(),
-            Align::from_bytes(1).unwrap(), // old_align anyway gets checked below by `deallocate`
-            new_ptr.into(),
-            new_align,
+            ptr,
+            new_ptr,
             old_size.min(new_size),
             /*nonoverlapping*/ true,
         )?;
@@ -310,6 +308,9 @@ pub fn deallocate(
     /// `Pointer` they need. And even if you already have a `Pointer`, call this method
     /// to make sure it is sufficiently aligned and not dangling.  Not doing that may
     /// cause ICEs.
+    ///
+    /// Most of the time you should use `check_mplace_access`, but when you just have a pointer,
+    /// this method is still appropriate.
     pub fn check_ptr_access(
         &self,
         sptr: Scalar<M::PointerTag>,
@@ -751,39 +752,26 @@ pub fn read_c_str(&self, ptr: Scalar<M::PointerTag>) -> InterpResult<'tcx, &[u8]
         self.get(ptr.alloc_id)?.read_c_str(self, ptr)
     }
 
-    /// Performs appropriate bounds checks.
+    /// Expects the caller to have checked bounds and alignment.
     pub fn copy(
         &mut self,
-        src: Scalar<M::PointerTag>,
-        src_align: Align,
-        dest: Scalar<M::PointerTag>,
-        dest_align: Align,
+        src: Pointer<M::PointerTag>,
+        dest: Pointer<M::PointerTag>,
         size: Size,
         nonoverlapping: bool,
     ) -> InterpResult<'tcx> {
-        self.copy_repeatedly(src, src_align, dest, dest_align, size, 1, nonoverlapping)
+        self.copy_repeatedly(src, dest, size, 1, nonoverlapping)
     }
 
-    /// Performs appropriate bounds checks.
+    /// Expects the caller to have checked bounds and alignment.
     pub fn copy_repeatedly(
         &mut self,
-        src: Scalar<M::PointerTag>,
-        src_align: Align,
-        dest: Scalar<M::PointerTag>,
-        dest_align: Align,
+        src: Pointer<M::PointerTag>,
+        dest: Pointer<M::PointerTag>,
         size: Size,
         length: u64,
         nonoverlapping: bool,
     ) -> InterpResult<'tcx> {
-        // We need to check *both* before early-aborting due to the size being 0.
-        let (src, dest) = match (self.check_ptr_access(src, size, src_align)?,
-                self.check_ptr_access(dest, size * length, dest_align)?)
-        {
-            (Some(src), Some(dest)) => (src, dest),
-            // One of the two sizes is 0.
-            _ => return Ok(()),
-        };
-
         // first copy the relocations to a temporary buffer, because
         // `get_bytes_mut` will clear the relocations, which is correct,
         // since we don't want to keep any relocations at the target.
index 68c9047f7b70842758c4b0484ac805532818e929..3d97132e53969472ea39c580fe253867067c3bc0 100644 (file)
@@ -123,23 +123,23 @@ pub enum Operand<Tag=(), Id=AllocId> {
 
 impl<Tag> Operand<Tag> {
     #[inline]
-    pub fn to_mem_place(self) -> MemPlace<Tag>
+    pub fn assert_mem_place(self) -> MemPlace<Tag>
         where Tag: ::std::fmt::Debug
     {
         match self {
             Operand::Indirect(mplace) => mplace,
-            _ => bug!("to_mem_place: expected Operand::Indirect, got {:?}", self),
+            _ => bug!("assert_mem_place: expected Operand::Indirect, got {:?}", self),
 
         }
     }
 
     #[inline]
-    pub fn to_immediate(self) -> Immediate<Tag>
+    pub fn assert_immediate(self) -> Immediate<Tag>
         where Tag: ::std::fmt::Debug
     {
         match self {
             Operand::Immediate(imm) => imm,
-            _ => bug!("to_immediate: expected Operand::Immediate, got {:?}", self),
+            _ => bug!("assert_immediate: expected Operand::Immediate, got {:?}", self),
 
         }
     }
@@ -214,6 +214,19 @@ pub(super) fn from_known_layout<'tcx>(
 }
 
 impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
+    /// Normalice `place.ptr` to a `Pointer` if this is a place and not a ZST.
+    /// Can be helpful to avoid lots of `force_ptr` calls later, if this place is used a lot.
+    #[inline]
+    pub fn force_op_ptr(
+        &self,
+        op: OpTy<'tcx, M::PointerTag>,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
+        match op.try_as_mplace() {
+            Ok(mplace) => Ok(self.force_mplace_ptr(mplace)?.into()),
+            Err(imm) => Ok(imm.into()), // Nothing to cast/force
+        }
+    }
+
     /// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`.
     /// Returns `None` if the layout does not permit loading this as a value.
     fn try_read_immediate_from_mplace(
@@ -224,9 +237,8 @@ fn try_read_immediate_from_mplace(
             // Don't touch unsized
             return Ok(None);
         }
-        let (ptr, ptr_align) = mplace.to_scalar_ptr_align();
 
-        let ptr = match self.memory.check_ptr_access(ptr, mplace.layout.size, ptr_align)? {
+        let ptr = match self.check_mplace_access(mplace, None)? {
             Some(ptr) => ptr,
             None => return Ok(Some(ImmTy { // zero-sized type
                 imm: Immediate::Scalar(Scalar::zst().into()),
@@ -396,7 +408,7 @@ pub fn operand_projection(
             } else {
                 // The rest should only occur as mplace, we do not use Immediates for types
                 // allowing such operations.  This matches place_projection forcing an allocation.
-                let mplace = base.to_mem_place();
+                let mplace = base.assert_mem_place();
                 self.mplace_projection(mplace, proj_elem)?.into()
             }
         })
index 3dee02989c9f93a865751bc3ffa7ffce8477b573..68382071b4a6720a391b12f7677df35adff812a6 100644 (file)
@@ -122,21 +122,6 @@ pub fn from_ptr(ptr: Pointer<Tag>, align: Align) -> Self {
         Self::from_scalar_ptr(ptr.into(), align)
     }
 
-    #[inline(always)]
-    pub fn to_scalar_ptr_align(self) -> (Scalar<Tag>, Align) {
-        assert!(self.meta.is_none());
-        (self.ptr, self.align)
-    }
-
-    /// metact the ptr part of the mplace
-    #[inline(always)]
-    pub fn to_ptr(self) -> InterpResult<'tcx, Pointer<Tag>> {
-        // At this point, we forget about the alignment information --
-        // the place has been turned into a reference, and no matter where it came from,
-        // it now must be aligned.
-        self.to_scalar_ptr_align().0.to_ptr()
-    }
-
     /// Turn a mplace into a (thin or fat) pointer, as a reference, pointing to the same space.
     /// This is the inverse of `ref_to_mplace`.
     #[inline(always)]
@@ -230,6 +215,7 @@ pub(super) fn vtable(self) -> Scalar<Tag> {
     }
 }
 
+// These are defined here because they produce a place.
 impl<'tcx, Tag: ::std::fmt::Debug + Copy> OpTy<'tcx, Tag> {
     #[inline(always)]
     pub fn try_as_mplace(self) -> Result<MPlaceTy<'tcx, Tag>, ImmTy<'tcx, Tag>> {
@@ -240,12 +226,12 @@ pub fn try_as_mplace(self) -> Result<MPlaceTy<'tcx, Tag>, ImmTy<'tcx, Tag>> {
     }
 
     #[inline(always)]
-    pub fn to_mem_place(self) -> MPlaceTy<'tcx, Tag> {
+    pub fn assert_mem_place(self) -> MPlaceTy<'tcx, Tag> {
         self.try_as_mplace().unwrap()
     }
 }
 
-impl<'tcx, Tag: ::std::fmt::Debug> Place<Tag> {
+impl<Tag: ::std::fmt::Debug> Place<Tag> {
     /// Produces a Place that will error if attempted to be read from or written to
     #[inline(always)]
     pub fn null(cx: &impl HasDataLayout) -> Self {
@@ -263,29 +249,19 @@ pub fn from_ptr(ptr: Pointer<Tag>, align: Align) -> Self {
     }
 
     #[inline]
-    pub fn to_mem_place(self) -> MemPlace<Tag> {
+    pub fn assert_mem_place(self) -> MemPlace<Tag> {
         match self {
             Place::Ptr(mplace) => mplace,
-            _ => bug!("to_mem_place: expected Place::Ptr, got {:?}", self),
+            _ => bug!("assert_mem_place: expected Place::Ptr, got {:?}", self),
 
         }
     }
-
-    #[inline]
-    pub fn to_scalar_ptr_align(self) -> (Scalar<Tag>, Align) {
-        self.to_mem_place().to_scalar_ptr_align()
-    }
-
-    #[inline]
-    pub fn to_ptr(self) -> InterpResult<'tcx, Pointer<Tag>> {
-        self.to_mem_place().to_ptr()
-    }
 }
 
 impl<'tcx, Tag: ::std::fmt::Debug> PlaceTy<'tcx, Tag> {
     #[inline]
-    pub fn to_mem_place(self) -> MPlaceTy<'tcx, Tag> {
-        MPlaceTy { mplace: self.place.to_mem_place(), layout: self.layout }
+    pub fn assert_mem_place(self) -> MPlaceTy<'tcx, Tag> {
+        MPlaceTy { mplace: self.place.assert_mem_place(), layout: self.layout }
     }
 }
 
@@ -301,8 +277,6 @@ impl<'mir, 'tcx, Tag, M> InterpCx<'mir, 'tcx, M>
 {
     /// Take a value, which represents a (thin or fat) reference, and make it a place.
     /// Alignment is just based on the type.  This is the inverse of `MemPlace::to_ref()`.
-    /// This does NOT call the "deref" machine hook, so it does NOT count as a
-    /// deref as far as Stacked Borrows is concerned.  Use `deref_operand` for that!
     pub fn ref_to_mplace(
         &self,
         val: ImmTy<'tcx, M::PointerTag>,
@@ -322,8 +296,8 @@ pub fn ref_to_mplace(
         Ok(MPlaceTy { mplace, layout })
     }
 
-    // Take an operand, representing a pointer, and dereference it to a place -- that
-    // will always be a MemPlace.  Lives in `place.rs` because it creates a place.
+    /// Take an operand, representing a pointer, and dereference it to a place -- that
+    /// will always be a MemPlace.  Lives in `place.rs` because it creates a place.
     pub fn deref_operand(
         &self,
         src: OpTy<'tcx, M::PointerTag>,
@@ -333,6 +307,36 @@ pub fn deref_operand(
         self.ref_to_mplace(val)
     }
 
+    /// Check if the given place is good for memory access with the given
+    /// size, falling back to the layout's size if `None` (in the latter case,
+    /// this must be a statically sized type).
+    ///
+    /// On success, returns `None` for zero-sized accesses (where nothing else is
+    /// left to do) and a `Pointer` to use for the actual access otherwise.
+    #[inline]
+    pub fn check_mplace_access(
+        &self,
+        place: MPlaceTy<'tcx, M::PointerTag>,
+        size: Option<Size>,
+    ) -> InterpResult<'tcx, Option<Pointer<M::PointerTag>>> {
+        let size = size.unwrap_or_else(|| {
+            assert!(!place.layout.is_unsized());
+            assert!(place.meta.is_none());
+            place.layout.size
+        });
+        self.memory.check_ptr_access(place.ptr, size, place.align)
+    }
+
+    /// Force `place.ptr` to a `Pointer`.
+    /// Can be helpful to avoid lots of `force_ptr` calls later, if this place is used a lot.
+    pub fn force_mplace_ptr(
+        &self,
+        mut place: MPlaceTy<'tcx, M::PointerTag>,
+    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+        place.mplace.ptr = self.force_ptr(place.mplace.ptr)?.into();
+        Ok(place)
+    }
+
     /// Offset a pointer to project to a field. Unlike `place_field`, this is always
     /// possible without allocating, so it can take `&self`. Also return the field's layout.
     /// This supports both struct and array fields.
@@ -741,14 +745,12 @@ fn write_immediate_to_mplace_no_validate(
         value: Immediate<M::PointerTag>,
         dest: MPlaceTy<'tcx, M::PointerTag>,
     ) -> InterpResult<'tcx> {
-        let (ptr, ptr_align) = dest.to_scalar_ptr_align();
         // Note that it is really important that the type here is the right one, and matches the
         // type things are read at. In case `src_val` is a `ScalarPair`, we don't do any magic here
         // to handle padding properly, which is only correct if we never look at this data with the
         // wrong type.
-        assert!(!dest.layout.is_unsized());
 
-        let ptr = match self.memory.check_ptr_access(ptr, dest.layout.size, ptr_align)? {
+        let ptr = match self.check_mplace_access(dest, None)? {
             Some(ptr) => ptr,
             None => return Ok(()), // zero-sized access
         };
@@ -850,14 +852,21 @@ fn copy_op_no_validate(
             dest.layout.size
         });
         assert_eq!(src.meta, dest.meta, "Can only copy between equally-sized instances");
+
+        let src = self.check_mplace_access(src, Some(size))?;
+        let dest = self.check_mplace_access(dest, Some(size))?;
+        let (src_ptr, dest_ptr) = match (src, dest) {
+            (Some(src_ptr), Some(dest_ptr)) => (src_ptr, dest_ptr),
+            (None, None) => return Ok(()), // zero-sized copy
+            _ => bug!("The pointers should both be Some or both None"),
+        };
+
         self.memory.copy(
-            src.ptr, src.align,
-            dest.ptr, dest.align,
+            src_ptr,
+            dest_ptr,
             size,
             /*nonoverlapping*/ true,
-        )?;
-
-        Ok(())
+        )
     }
 
     /// Copies the data from an operand to a place. The layouts may disagree, but they must
index dc5302eb18fc4b2d1ac407b9afd805a55edfc37c..246c90ba48e3aa3661ca61a81dbaf4ea932a0a0a 100644 (file)
@@ -209,17 +209,18 @@ fn eval_rvalue_into_place(
                 let dest = self.force_allocation(dest)?;
                 let length = dest.len(self)?;
 
-                if length > 0 {
-                    // write the first
+                if let Some(first_ptr) = self.check_mplace_access(dest, None)? {
+                    // Write the first.
                     let first = self.mplace_field(dest, 0)?;
                     self.copy_op(op, first.into())?;
 
                     if length > 1 {
-                        // copy the rest
-                        let (dest, dest_align) = first.to_scalar_ptr_align();
-                        let rest = dest.ptr_offset(first.layout.size, self)?;
+                        let elem_size = first.layout.size;
+                        // Copy the rest. This is performance-sensitive code
+                        // for big static/const arrays!
+                        let rest_ptr = first_ptr.offset(elem_size, self)?;
                         self.memory.copy_repeatedly(
-                            dest, dest_align, rest, dest_align, first.layout.size, length - 1, true
+                            first_ptr, rest_ptr, elem_size, length - 1, /*nonoverlapping:*/true
                         )?;
                     }
                 }
index 0ab428628de688d12ee78c08ee383225453ad839..c11e5e119237f48dba3930ba73e9ce57fb0da7bc 100644 (file)
@@ -426,7 +426,7 @@ fn eval_fn_call(
                     }
                     None => {
                         // Unsized self.
-                        args[0].to_mem_place()
+                        args[0].assert_mem_place()
                     }
                 };
                 // Find and consult vtable
index 34892f5b8ca01bda2f702e94523609fdf6e423f5..00107a536ba26522d1e317b215342d9d069799d3 100644 (file)
@@ -440,9 +440,16 @@ fn visit_primitive(&mut self, value: OpTy<'tcx, M::PointerTag>) -> InterpResult<
                             }
                         }
                     }
-                    // Check if we have encountered this pointer+layout combination
-                    // before.  Proceed recursively even for ZST, no
-                    // reason to skip them! E.g., `!` is a ZST and we want to validate it.
+                    // Proceed recursively even for ZST, no reason to skip them!
+                    // `!` is a ZST and we want to validate it.
+                    // Normalize before handing `place` to tracking because that will
+                    // check for duplicates.
+                    let place = if size.bytes() > 0 {
+                        self.ecx.force_mplace_ptr(place)
+                            .expect("we already bounds-checked")
+                    } else {
+                        place
+                    };
                     let path = &self.path;
                     ref_tracking.track(place, || {
                         // We need to clone the path anyway, make sure it gets created
@@ -548,7 +555,7 @@ fn visit_aggregate(
     ) -> InterpResult<'tcx> {
         match op.layout.ty.sty {
             ty::Str => {
-                let mplace = op.to_mem_place(); // strings are never immediate
+                let mplace = op.assert_mem_place(); // strings are never immediate
                 try_validation!(self.ecx.read_str(mplace),
                     "uninitialized or non-UTF-8 data in str", self.path);
             }
@@ -565,7 +572,7 @@ fn visit_aggregate(
                     return Ok(());
                 }
                 // non-ZST array cannot be immediate, slices are never immediate
-                let mplace = op.to_mem_place();
+                let mplace = op.assert_mem_place();
                 // This is the length of the array/slice.
                 let len = mplace.len(self.ecx)?;
                 // zero length slices have nothing to be checked
@@ -576,7 +583,7 @@ fn visit_aggregate(
                 let ty_size = self.ecx.layout_of(tys)?.size;
                 // This is the size in bytes of the whole array.
                 let size = ty_size * len;
-
+                // Size is not 0, get a pointer.
                 let ptr = self.ecx.force_ptr(mplace.ptr)?;
 
                 // NOTE: Keep this in sync with the handling of integer and float
@@ -633,7 +640,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// `ref_tracking_for_consts` can be `None` to avoid recursive checking below references.
     /// This also toggles between "run-time" (no recursion) and "compile-time" (with recursion)
     /// validation (e.g., pointer values are fine in integers at runtime) and various other const
-    /// specific validation checks
+    /// specific validation checks.
     pub fn validate_operand(
         &self,
         op: OpTy<'tcx, M::PointerTag>,
@@ -652,6 +659,9 @@ pub fn validate_operand(
             ecx: self,
         };
 
+        // Try to cast to ptr *once* instead of all the time.
+        let op = self.force_op_ptr(op).unwrap_or(op);
+
         // Run it
         visitor.visit_value(op)
     }
index 783d2522637352977299bba19f493ff3a9b105f7..91fbd307db12123e04108f0066eb37877ea20e28 100644 (file)
@@ -242,7 +242,7 @@ fn walk_value(&mut self, v: Self::V) -> InterpResult<'tcx>
                 match v.layout().ty.sty {
                     ty::Dynamic(..) => {
                         // immediate trait objects are not a thing
-                        let dest = v.to_op(self.ecx())?.to_mem_place();
+                        let dest = v.to_op(self.ecx())?.assert_mem_place();
                         let inner = self.ecx().unpack_dyn_trait(dest)?.1;
                         trace!("walk_value: dyn object layout: {:#?}", inner.layout);
                         // recurse with the inner type
@@ -316,7 +316,7 @@ fn walk_value(&mut self, v: Self::V) -> InterpResult<'tcx>
                             MPlaceTy::dangling(v.layout(), self.ecx())
                         } else {
                             // non-ZST array/slice/str cannot be immediate
-                            v.to_op(self.ecx())?.to_mem_place()
+                            v.to_op(self.ecx())?.assert_mem_place()
                         };
                         // Now we can go over all the fields.
                         let iter = self.ecx().mplace_array_fields(mplace)?
index 4b9475ba627183092b85f9a2d406018893c8c6ed..42c6f24b36a0cfc9f090bda87b7f82256d9a7208 100644 (file)
@@ -14,6 +14,8 @@ fn main() {
     //~^ ERROR `a` does not live long enough [E0597]
     match b {
         <() as Foo<'static>>::C => { }
+        //~^ WARN must be annotated with `#[derive(PartialEq, Eq)]`
+        //~| WARN will become a hard error in a future release
         _ => { }
     }
 }
index bf3e58e8cdb19b1c5cfea5247b1bba8e6cc4af73..c0f702e4fab2332dea4da5df735ec0d79a0c83d1 100644 (file)
@@ -1,3 +1,13 @@
+warning: to use a constant of type `std::cell::Cell` in a pattern, `std::cell::Cell` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/issue-55511.rs:16:9
+   |
+LL |         <() as Foo<'static>>::C => { }
+   |         ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = 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 <https://github.com/rust-lang/rust/issues/62411>
+
 error[E0597]: `a` does not live long enough
   --> $DIR/issue-55511.rs:13:28
    |
diff --git a/src/test/ui/rfc1445/allow-hide-behind-direct-unsafe-ptr-embedded.rs b/src/test/ui/rfc1445/allow-hide-behind-direct-unsafe-ptr-embedded.rs
new file mode 100644 (file)
index 0000000..b90a750
--- /dev/null
@@ -0,0 +1,24 @@
+// Test explores how `#[structral_match]` behaves in tandem with
+// `*const` and `*mut` pointers.
+
+// run-pass
+
+struct NoDerive(i32);
+
+// This impl makes NoDerive irreflexive
+// (which doesn't matter here because `<*const T>::eq` won't recur on `T`).
+impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } }
+
+impl Eq for NoDerive { }
+
+#[derive(PartialEq, Eq)]
+struct WrapEmbedded(*const NoDerive);
+
+const WRAP_UNSAFE_EMBEDDED: WrapEmbedded = WrapEmbedded(std::ptr::null());
+
+fn main() {
+    match WRAP_UNSAFE_EMBEDDED {
+        WRAP_UNSAFE_EMBEDDED => { println!("WRAP_UNSAFE_EMBEDDED correctly matched itself"); }
+        _ => { panic!("WRAP_UNSAFE_EMBEDDED did not match itself"); }
+    }
+}
diff --git a/src/test/ui/rfc1445/allow-hide-behind-direct-unsafe-ptr-param.rs b/src/test/ui/rfc1445/allow-hide-behind-direct-unsafe-ptr-param.rs
new file mode 100644 (file)
index 0000000..1076b9f
--- /dev/null
@@ -0,0 +1,24 @@
+// Test explores how `#[structral_match]` behaves in tandem with
+// `*const` and `*mut` pointers.
+
+// run-pass
+
+struct NoDerive(i32);
+
+// This impl makes NoDerive irreflexive
+// (which doesn't matter here because `<*const T>::eq` won't recur on `T`).
+impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } }
+
+impl Eq for NoDerive { }
+
+#[derive(PartialEq, Eq)]
+struct WrapParam<X>(*const X);
+
+const WRAP_UNSAFE_PARAM: WrapParam<NoDerive> = WrapParam(std::ptr::null());
+
+fn main() {
+    match WRAP_UNSAFE_PARAM {
+        WRAP_UNSAFE_PARAM => { println!("WRAP_UNSAFE_PARAM correctly matched itself"); }
+        _ => { panic!("WRAP_UNSAFE_PARAM did not match itself"); }
+    }
+}
diff --git a/src/test/ui/rfc1445/allow-hide-behind-indirect-unsafe-ptr-embedded.rs b/src/test/ui/rfc1445/allow-hide-behind-indirect-unsafe-ptr-embedded.rs
new file mode 100644 (file)
index 0000000..a4b832d
--- /dev/null
@@ -0,0 +1,24 @@
+// Test explores how `#[structral_match]` behaves in tandem with
+// `*const` and `*mut` pointers.
+
+// run-pass
+
+struct NoDerive(i32);
+
+// This impl makes NoDerive irreflexive
+// (which doesn't matter here because `<*const T>::eq` won't recur on `T`).
+impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } }
+
+impl Eq for NoDerive { }
+
+#[derive(PartialEq, Eq)]
+struct WrapEmbedded(*const NoDerive);
+
+const WRAP_UNSAFE_EMBEDDED: & &WrapEmbedded = & &WrapEmbedded(std::ptr::null());
+
+fn main() {
+    match WRAP_UNSAFE_EMBEDDED {
+        WRAP_UNSAFE_EMBEDDED => { println!("WRAP_UNSAFE_EMBEDDED correctly matched itself"); }
+        _ => { panic!("WRAP_UNSAFE_EMBEDDED did not match itself"); }
+    }
+}
diff --git a/src/test/ui/rfc1445/allow-hide-behind-indirect-unsafe-ptr-param.rs b/src/test/ui/rfc1445/allow-hide-behind-indirect-unsafe-ptr-param.rs
new file mode 100644 (file)
index 0000000..47b70e2
--- /dev/null
@@ -0,0 +1,24 @@
+// Test explores how `#[structral_match]` behaves in tandem with
+// `*const` and `*mut` pointers.
+
+// run-pass
+
+struct NoDerive(i32);
+
+// This impl makes NoDerive irreflexive
+// (which doesn't matter here because `<*const T>::eq` won't recur on `T`).
+impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } }
+
+impl Eq for NoDerive { }
+
+#[derive(PartialEq, Eq)]
+struct WrapParam<X>(*const X);
+
+const WRAP_UNSAFE_PARAM: & &WrapParam<NoDerive> = & &WrapParam(std::ptr::null());
+
+fn main() {
+    match WRAP_UNSAFE_PARAM {
+        WRAP_UNSAFE_PARAM => { println!("WRAP_UNSAFE_PARAM correctly matched itself"); }
+        _ => { panic!("WRAP_UNSAFE_PARAM did not match itself"); }
+    }
+}
diff --git a/src/test/ui/rfc1445/cant-hide-behind-direct-struct-embedded.rs b/src/test/ui/rfc1445/cant-hide-behind-direct-struct-embedded.rs
new file mode 100644 (file)
index 0000000..b8949ae
--- /dev/null
@@ -0,0 +1,26 @@
+// This is part of a set of tests exploring the different ways a
+// `#[structural_match]` ADT might try to hold a
+// non-`#[structural_match]` in hidden manner that lets matches
+// through that we had intended to reject.
+//
+// See discussion on rust-lang/rust#62307 and rust-lang/rust#62339
+
+struct NoDerive(i32);
+
+// This impl makes NoDerive irreflexive.
+impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } }
+
+impl Eq for NoDerive { }
+
+#[derive(PartialEq, Eq)]
+struct WrapInline(NoDerive);
+
+const WRAP_DIRECT_INLINE: WrapInline = WrapInline(NoDerive(0));
+
+fn main() {
+    match WRAP_DIRECT_INLINE {
+        WRAP_DIRECT_INLINE => { panic!("WRAP_DIRECT_INLINE matched itself"); }
+        //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+        _ => { println!("WRAP_DIRECT_INLINE did not match itself"); }
+    }
+}
diff --git a/src/test/ui/rfc1445/cant-hide-behind-direct-struct-embedded.stderr b/src/test/ui/rfc1445/cant-hide-behind-direct-struct-embedded.stderr
new file mode 100644 (file)
index 0000000..c73a6cf
--- /dev/null
@@ -0,0 +1,8 @@
+error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/cant-hide-behind-direct-struct-embedded.rs:22:9
+   |
+LL |         WRAP_DIRECT_INLINE => { panic!("WRAP_DIRECT_INLINE matched itself"); }
+   |         ^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/rfc1445/cant-hide-behind-direct-struct-param.rs b/src/test/ui/rfc1445/cant-hide-behind-direct-struct-param.rs
new file mode 100644 (file)
index 0000000..a3a615e
--- /dev/null
@@ -0,0 +1,26 @@
+// This is part of a set of tests exploring the different ways a
+// `#[structural_match]` ADT might try to hold a
+// non-`#[structural_match]` in hidden manner that lets matches
+// through that we had intended to reject.
+//
+// See discussion on rust-lang/rust#62307 and rust-lang/rust#62339
+
+struct NoDerive(i32);
+
+// This impl makes NoDerive irreflexive.
+impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } }
+
+impl Eq for NoDerive { }
+
+#[derive(PartialEq, Eq)]
+struct WrapParam<T>(T);
+
+const WRAP_DIRECT_PARAM: WrapParam<NoDerive> = WrapParam(NoDerive(0));
+
+fn main() {
+    match WRAP_DIRECT_PARAM {
+        WRAP_DIRECT_PARAM => { panic!("WRAP_DIRECT_PARAM matched itself"); }
+        //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+        _ => { println!("WRAP_DIRECT_PARAM did not match itself"); }
+    }
+}
diff --git a/src/test/ui/rfc1445/cant-hide-behind-direct-struct-param.stderr b/src/test/ui/rfc1445/cant-hide-behind-direct-struct-param.stderr
new file mode 100644 (file)
index 0000000..6fdf9db
--- /dev/null
@@ -0,0 +1,8 @@
+error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/cant-hide-behind-direct-struct-param.rs:22:9
+   |
+LL |         WRAP_DIRECT_PARAM => { panic!("WRAP_DIRECT_PARAM matched itself"); }
+   |         ^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-embedded.rs b/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-embedded.rs
new file mode 100644 (file)
index 0000000..b6d9c52
--- /dev/null
@@ -0,0 +1,29 @@
+// This is part of a set of tests exploring the different ways a
+// `#[structural_match]` ADT might try to hold a
+// non-`#[structural_match]` in hidden manner that lets matches
+// through that we had intended to reject.
+//
+// See discussion on rust-lang/rust#62307 and rust-lang/rust#62339
+
+// run-pass
+
+struct NoDerive(i32);
+
+// This impl makes NoDerive irreflexive.
+impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } }
+
+impl Eq for NoDerive { }
+
+#[derive(PartialEq, Eq)]
+struct WrapInline<'a>(&'a &'a NoDerive);
+
+const WRAP_DOUBLY_INDIRECT_INLINE: & &WrapInline = & &WrapInline(& & NoDerive(0));
+
+fn main() {
+    match WRAP_DOUBLY_INDIRECT_INLINE {
+        WRAP_DOUBLY_INDIRECT_INLINE => { panic!("WRAP_DOUBLY_INDIRECT_INLINE matched itself"); }
+        //~^ WARN must be annotated with `#[derive(PartialEq, Eq)]`
+        //~| WARN will become a hard error in a future release
+        _ => { println!("WRAP_DOUBLY_INDIRECT_INLINE correctly did not match itself"); }
+    }
+}
diff --git a/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-embedded.stderr b/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-embedded.stderr
new file mode 100644 (file)
index 0000000..3de63f4
--- /dev/null
@@ -0,0 +1,10 @@
+warning: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/cant-hide-behind-doubly-indirect-embedded.rs:24:9
+   |
+LL |         WRAP_DOUBLY_INDIRECT_INLINE => { panic!("WRAP_DOUBLY_INDIRECT_INLINE matched itself"); }
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = 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 <https://github.com/rust-lang/rust/issues/62411>
+
diff --git a/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-param.rs b/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-param.rs
new file mode 100644 (file)
index 0000000..804d336
--- /dev/null
@@ -0,0 +1,29 @@
+// This is part of a set of tests exploring the different ways a
+// `#[structural_match]` ADT might try to hold a
+// non-`#[structural_match]` in hidden manner that lets matches
+// through that we had intended to reject.
+//
+// See discussion on rust-lang/rust#62307 and rust-lang/rust#62339
+
+// run-pass
+
+struct NoDerive(i32);
+
+// This impl makes NoDerive irreflexive.
+impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } }
+
+impl Eq for NoDerive { }
+
+#[derive(PartialEq, Eq)]
+struct WrapParam<'a, T>(&'a &'a T);
+
+const WRAP_DOUBLY_INDIRECT_PARAM: & &WrapParam<NoDerive> = & &WrapParam(& & NoDerive(0));
+
+fn main() {
+    match WRAP_DOUBLY_INDIRECT_PARAM {
+        WRAP_DOUBLY_INDIRECT_PARAM => { panic!("WRAP_DOUBLY_INDIRECT_PARAM matched itself"); }
+        //~^ WARN must be annotated with `#[derive(PartialEq, Eq)]`
+        //~| WARN will become a hard error in a future release
+        _ => { println!("WRAP_DOUBLY_INDIRECT_PARAM correctly did not match itself"); }
+    }
+}
diff --git a/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-param.stderr b/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-param.stderr
new file mode 100644 (file)
index 0000000..ee4652d
--- /dev/null
@@ -0,0 +1,10 @@
+warning: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/cant-hide-behind-doubly-indirect-param.rs:24:9
+   |
+LL |         WRAP_DOUBLY_INDIRECT_PARAM => { panic!("WRAP_DOUBLY_INDIRECT_PARAM matched itself"); }
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = 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 <https://github.com/rust-lang/rust/issues/62411>
+
diff --git a/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-embedded.rs b/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-embedded.rs
new file mode 100644 (file)
index 0000000..85d2e65
--- /dev/null
@@ -0,0 +1,29 @@
+// This is part of a set of tests exploring the different ways a
+// `#[structural_match]` ADT might try to hold a
+// non-`#[structural_match]` in hidden manner that lets matches
+// through that we had intended to reject.
+//
+// See discussion on rust-lang/rust#62307 and rust-lang/rust#62339
+
+// run-pass
+
+struct NoDerive(i32);
+
+// This impl makes NoDerive irreflexive.
+impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } }
+
+impl Eq for NoDerive { }
+
+#[derive(PartialEq, Eq)]
+struct WrapInline(NoDerive);
+
+const WRAP_INDIRECT_INLINE: & &WrapInline = & &WrapInline(NoDerive(0));
+
+fn main() {
+    match WRAP_INDIRECT_INLINE {
+        WRAP_INDIRECT_INLINE => { panic!("WRAP_INDIRECT_INLINE matched itself"); }
+        //~^ WARN must be annotated with `#[derive(PartialEq, Eq)]`
+        //~| WARN will become a hard error in a future release
+        _ => { println!("WRAP_INDIRECT_INLINE did not match itself"); }
+    }
+}
diff --git a/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-embedded.stderr b/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-embedded.stderr
new file mode 100644 (file)
index 0000000..eb74684
--- /dev/null
@@ -0,0 +1,10 @@
+warning: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/cant-hide-behind-indirect-struct-embedded.rs:24:9
+   |
+LL |         WRAP_INDIRECT_INLINE => { panic!("WRAP_INDIRECT_INLINE matched itself"); }
+   |         ^^^^^^^^^^^^^^^^^^^^
+   |
+   = 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 <https://github.com/rust-lang/rust/issues/62411>
+
diff --git a/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-param.rs b/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-param.rs
new file mode 100644 (file)
index 0000000..849aa14
--- /dev/null
@@ -0,0 +1,29 @@
+// This is part of a set of tests exploring the different ways a
+// `#[structural_match]` ADT might try to hold a
+// non-`#[structural_match]` in hidden manner that lets matches
+// through that we had intended to reject.
+//
+// See discussion on rust-lang/rust#62307 and rust-lang/rust#62339
+
+// run-pass
+
+struct NoDerive(i32);
+
+// This impl makes NoDerive irreflexive.
+impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } }
+
+impl Eq for NoDerive { }
+
+#[derive(PartialEq, Eq)]
+struct WrapParam<T>(T);
+
+const WRAP_INDIRECT_PARAM: & &WrapParam<NoDerive> = & &WrapParam(NoDerive(0));
+
+fn main() {
+    match WRAP_INDIRECT_PARAM {
+        WRAP_INDIRECT_PARAM => { panic!("WRAP_INDIRECT_PARAM matched itself"); }
+        //~^ WARN must be annotated with `#[derive(PartialEq, Eq)]`
+        //~| WARN will become a hard error in a future release
+        _ => { println!("WRAP_INDIRECT_PARAM correctly did not match itself"); }
+    }
+}
diff --git a/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-param.stderr b/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-param.stderr
new file mode 100644 (file)
index 0000000..8a16556
--- /dev/null
@@ -0,0 +1,10 @@
+warning: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/cant-hide-behind-indirect-struct-param.rs:24:9
+   |
+LL |         WRAP_INDIRECT_PARAM => { panic!("WRAP_INDIRECT_PARAM matched itself"); }
+   |         ^^^^^^^^^^^^^^^^^^^
+   |
+   = 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 <https://github.com/rust-lang/rust/issues/62411>
+
diff --git a/src/test/ui/rfc1445/issue-61118-match-slice-forbidden-without-eq.rs b/src/test/ui/rfc1445/issue-61118-match-slice-forbidden-without-eq.rs
new file mode 100644 (file)
index 0000000..9a96628
--- /dev/null
@@ -0,0 +1,19 @@
+// Issue 61118 pointed out a case where we hit an ICE during code gen:
+// the compiler assumed that `PartialEq` was always implemented on any
+// use of a `const` item in a pattern context, but the pre-existing
+// checking for the presence of `#[structural_match]` was too shallow
+// (see rust-lang/rust#62307), and so we hit cases where we were
+// trying to dispatch to `PartialEq` on types that did not implement
+// that trait.
+
+struct B(i32);
+
+const A: &[B] = &[];
+
+pub fn main() {
+    match &[][..] {
+        A => (),
+        //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+        _ => (),
+    }
+}
diff --git a/src/test/ui/rfc1445/issue-61118-match-slice-forbidden-without-eq.stderr b/src/test/ui/rfc1445/issue-61118-match-slice-forbidden-without-eq.stderr
new file mode 100644 (file)
index 0000000..e8141f6
--- /dev/null
@@ -0,0 +1,8 @@
+error: to use a constant of type `B` in a pattern, `B` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/issue-61118-match-slice-forbidden-without-eq.rs:15:9
+   |
+LL |         A => (),
+   |         ^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/rfc1445/issue-62307-match-ref-ref-forbidden-without-eq.rs b/src/test/ui/rfc1445/issue-62307-match-ref-ref-forbidden-without-eq.rs
new file mode 100644 (file)
index 0000000..7646f75
--- /dev/null
@@ -0,0 +1,43 @@
+// RFC 1445 introduced `#[structural_match]`; this attribute must
+// appear on the `struct`/`enum` definition for any `const` used in a
+// pattern.
+//
+// This is our (forever-unstable) way to mark a datatype as having a
+// `PartialEq` implementation that is equivalent to recursion over its
+// substructure. This avoids (at least in the short term) any need to
+// resolve the question of what semantics is used for such matching.
+// (See RFC 1445 for more details and discussion.)
+
+// Issue 62307 pointed out a case where the checking for
+// `#[structural_match]` was too shallow.
+
+// run-pass
+
+#[derive(Debug)]
+struct B(i32);
+
+// Overriding `PartialEq` to use this strange notion of "equality" exposes
+// whether `match` is using structural-equality or method-dispatch
+// under the hood, which is the antithesis of rust-lang/rfcs#1445
+impl PartialEq for B {
+    fn eq(&self, other: &B) -> bool { std::cmp::min(self.0, other.0) == 0 }
+}
+
+fn main() {
+    const RR_B0: & & B = & & B(0);
+    const RR_B1: & & B = & & B(1);
+
+    match RR_B0 {
+        RR_B1 => { println!("CLAIM RR0: {:?} matches {:?}", RR_B1, RR_B0); }
+        //~^ WARN must be annotated with `#[derive(PartialEq, Eq)]`
+        //~| WARN will become a hard error in a future release
+        _ => { }
+    }
+
+    match RR_B1 {
+        RR_B1 => { println!("CLAIM RR1: {:?} matches {:?}", RR_B1, RR_B1); }
+        //~^ WARN must be annotated with `#[derive(PartialEq, Eq)]`
+        //~| WARN will become a hard error in a future release
+        _ => { }
+    }
+}
diff --git a/src/test/ui/rfc1445/issue-62307-match-ref-ref-forbidden-without-eq.stderr b/src/test/ui/rfc1445/issue-62307-match-ref-ref-forbidden-without-eq.stderr
new file mode 100644 (file)
index 0000000..ba0275f
--- /dev/null
@@ -0,0 +1,19 @@
+warning: to use a constant of type `B` in a pattern, `B` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/issue-62307-match-ref-ref-forbidden-without-eq.rs:31:9
+   |
+LL |         RR_B1 => { println!("CLAIM RR0: {:?} matches {:?}", RR_B1, RR_B0); }
+   |         ^^^^^
+   |
+   = 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 <https://github.com/rust-lang/rust/issues/62411>
+
+warning: to use a constant of type `B` in a pattern, `B` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/issue-62307-match-ref-ref-forbidden-without-eq.rs:38:9
+   |
+LL |         RR_B1 => { println!("CLAIM RR1: {:?} matches {:?}", RR_B1, RR_B1); }
+   |         ^^^^^
+   |
+   = 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 <https://github.com/rust-lang/rust/issues/62411>
+
diff --git a/src/test/ui/rfc1445/match-empty-array-allowed-without-eq-issue-62336.rs b/src/test/ui/rfc1445/match-empty-array-allowed-without-eq-issue-62336.rs
new file mode 100644 (file)
index 0000000..7ba0f3a
--- /dev/null
@@ -0,0 +1,17 @@
+// Pre-existing behavior has been to reject patterns with consts
+// denoting non-empty arrays of non-`Eq` types, but *accept* empty
+// arrays of such types.
+//
+// See rust-lang/rust#62336.
+
+// run-pass
+
+#[derive(PartialEq, Debug)]
+struct B(i32);
+
+fn main() {
+    const FOO: [B; 0] = [];
+    match [] {
+        FOO => { }
+    }
+}
diff --git a/src/test/ui/rfc1445/match-nonempty-array-forbidden-without-eq.rs b/src/test/ui/rfc1445/match-nonempty-array-forbidden-without-eq.rs
new file mode 100644 (file)
index 0000000..3d56fb0
--- /dev/null
@@ -0,0 +1,19 @@
+// Issue 62307 pointed out a case where the checking for
+// `#[structural_match]` was too shallow.
+//
+// Here we check similar behavior for non-empty arrays of types that
+// do not derive `Eq`.
+//
+// (Current behavior for empty arrays differs and thus is not tested
+// here; see rust-lang/rust#62336.)
+
+#[derive(PartialEq, Debug)]
+struct B(i32);
+
+fn main() {
+    const FOO: [B; 1] = [B(0)];
+    match [B(1)] {
+        FOO => { }
+        //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+    }
+}
diff --git a/src/test/ui/rfc1445/match-nonempty-array-forbidden-without-eq.stderr b/src/test/ui/rfc1445/match-nonempty-array-forbidden-without-eq.stderr
new file mode 100644 (file)
index 0000000..371f8a0
--- /dev/null
@@ -0,0 +1,8 @@
+error: to use a constant of type `B` in a pattern, `B` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/match-nonempty-array-forbidden-without-eq.rs:16:9
+   |
+LL |         FOO => { }
+   |         ^^^
+
+error: aborting due to previous error
+