diff options
| author | Ralf Jung <post@ralfj.de> | 2024-07-13 18:03:05 +0200 |
|---|---|---|
| committer | Ralf Jung <post@ralfj.de> | 2024-07-18 11:58:16 +0200 |
| commit | 86ce911f90d9498b33738655c8cfed2f637c335a (patch) | |
| tree | 6bff3cc5cda91533e2f7641c5de56291842432ba | |
| parent | e613bc92a12c176d0e206b2f83b1bae8011e90c1 (diff) | |
pattern lowering: make sure we never call user-defined PartialEq instances
| -rw-r--r-- | compiler/rustc_middle/src/thir.rs | 11 | ||||
| -rw-r--r-- | compiler/rustc_mir_build/src/build/matches/test.rs | 45 | ||||
| -rw-r--r-- | compiler/rustc_pattern_analysis/src/rustc.rs | 18 |
3 files changed, 35 insertions, 39 deletions
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index c97af68c29e..b80d00719ee 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -783,16 +783,13 @@ pub enum PatKind<'tcx> { }, /// One of the following: - /// * `&str` (represented as a valtree), which will be handled as a string pattern and thus - /// exhaustiveness checking will detect if you use the same string twice in different - /// patterns. + /// * `&str`/`&[u8]` (represented as a valtree), which will be handled as a string/slice pattern + /// and thus exhaustiveness checking will detect if you use the same string/slice twice in + /// different patterns. /// * integer, bool, char or float (represented as a valtree), which will be handled by /// exhaustiveness to cover exactly its own value, similar to `&str`, but these values are /// much simpler. - /// * Opaque constants (represented as `mir::ConstValue`), that must not be matched - /// structurally. So anything that does not derive `PartialEq` and `Eq`. - /// - /// These are always compared with the matched place using (the semantics of) `PartialEq`. + /// * `String`, if `string_deref_patterns` is enabled. Constant { value: mir::Const<'tcx>, }, diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index d29874a5ad4..5aed2537750 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -144,7 +144,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { && tcx.is_lang_item(def.did(), LangItem::String) { if !tcx.features().string_deref_patterns { - bug!( + span_bug!( + test.span, "matching on `String` went through without enabling string_deref_patterns" ); } @@ -432,40 +433,28 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - match *ty.kind() { - ty::Ref(_, deref_ty, _) => ty = deref_ty, - _ => { - // non_scalar_compare called on non-reference type - let temp = self.temp(ty, source_info.span); - self.cfg.push_assign(block, source_info, temp, Rvalue::Use(expect)); - let ref_ty = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, ty); - let ref_temp = self.temp(ref_ty, source_info.span); - - self.cfg.push_assign( - block, - source_info, - ref_temp, - Rvalue::Ref(self.tcx.lifetimes.re_erased, BorrowKind::Shared, temp), - ); - expect = Operand::Move(ref_temp); - - let ref_temp = self.temp(ref_ty, source_info.span); - self.cfg.push_assign( - block, - source_info, - ref_temp, - Rvalue::Ref(self.tcx.lifetimes.re_erased, BorrowKind::Shared, val), - ); - val = ref_temp; + // Figure out the type on which we are calling `PartialEq`. This involves an extra wrapping + // reference: we can only compare two `&T`, and then compare_ty will be `T`. + // Make sure that we do *not* call any user-defined code here. + // The only types that can end up here are string and byte literals, + // which have their comparison defined in `core`. + // (Interestingly this means that exhaustiveness analysis relies, for soundness, + // on the `PartialEq` impls for `str` and `[u8]` to b correct!) + let compare_ty = match *ty.kind() { + ty::Ref(_, deref_ty, _) + if deref_ty == self.tcx.types.str_ || deref_ty != self.tcx.types.u8 => + { + deref_ty } - } + _ => span_bug!(source_info.span, "invalid type for non-scalar compare: {}", ty), + }; let eq_def_id = self.tcx.require_lang_item(LangItem::PartialEq, Some(source_info.span)); let method = trait_method( self.tcx, eq_def_id, sym::eq, - self.tcx.with_opt_host_effect_param(self.def_id, eq_def_id, [ty, ty]), + self.tcx.with_opt_host_effect_param(self.def_id, eq_def_id, [compare_ty, compare_ty]), ); let bool_ty = self.tcx.types.bool; diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index d4dd4dd858c..d17ee8bff50 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -462,7 +462,12 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { // This is a box pattern. ty::Adt(adt, ..) if adt.is_box() => Struct, ty::Ref(..) => Ref, - _ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, ty), + _ => span_bug!( + pat.span, + "pattern has unexpected type: pat: {:?}, ty: {:?}", + pat.kind, + ty.inner() + ), }; } PatKind::DerefPattern { .. } => { @@ -518,7 +523,12 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { .map(|ipat| self.lower_pat(&ipat.pattern).at_index(ipat.field.index())) .collect(); } - _ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, ty), + _ => span_bug!( + pat.span, + "pattern has unexpected type: pat: {:?}, ty: {}", + pat.kind, + ty.inner() + ), } } PatKind::Constant { value } => { @@ -663,7 +673,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } } } - _ => bug!("invalid type for range pattern: {}", ty.inner()), + _ => span_bug!(pat.span, "invalid type for range pattern: {}", ty.inner()), }; fields = vec![]; arity = 0; @@ -674,7 +684,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { Some(length.eval_target_usize(cx.tcx, cx.param_env) as usize) } ty::Slice(_) => None, - _ => span_bug!(pat.span, "bad ty {:?} for slice pattern", ty), + _ => span_bug!(pat.span, "bad ty {} for slice pattern", ty.inner()), }; let kind = if slice.is_some() { SliceKind::VarLen(prefix.len(), suffix.len()) |
