Skip to content

Commit b560801

Browse files
committed
Rewrite with future-compat lint for indirect pattern omitting
`#[structural_match]`. Outline of changes: * Recur as deeply as necessary when searching for `#[structural_match]`. * `#[structural_match]`: handle case of `const A: & &Wrap(NoDerive)` by including the fields of an ADT during traversal of input type. (We continue to not traverse the substs of an ADT, though, so that we continue to handle `PhantomData<NoDerive>` and `*NoDerive` properly.) * Refactored code to use `match` instead of `if let`. This ends up *with less* right-ward drift by moving the handling of the main *`ty::Adt` case *outside* the match. * Using lint (rather than hard error) mmeans we need to check that type is `PartialEq` to avoid ICE'ing the compiler in scneario where MIR codegen dispatches to `PartialEq::eq`. Added said check, and fatal error in that case.
1 parent 36777f1 commit b560801

File tree

2 files changed

+185
-6
lines changed

2 files changed

+185
-6
lines changed

src/librustc_mir/hair/pattern/check_match.rs

+2
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
170170
let mut patcx = PatternContext::new(self.tcx,
171171
self.param_env.and(self.identity_substs),
172172
self.tables);
173+
patcx.include_lint_checks();
173174
let pattern = expand_pattern(cx, patcx.lower_pattern(&pat));
174175
if !patcx.errors.is_empty() {
175176
patcx.report_inlining_errors(pat.span);
@@ -266,6 +267,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
266267
let mut patcx = PatternContext::new(self.tcx,
267268
self.param_env.and(self.identity_substs),
268269
self.tables);
270+
patcx.include_lint_checks();
269271
let pattern = patcx.lower_pattern(pat);
270272
let pattern_ty = pattern.ty;
271273
let pats: Matrix<'_, '_> = vec![smallvec![

src/librustc_mir/hair/pattern/mod.rs

+183-6
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ use crate::const_eval::const_variant_index;
1010
use crate::hair::util::UserAnnotatedTyHelpers;
1111
use crate::hair::constant::*;
1212

13+
use rustc::lint;
1314
use rustc::mir::{Field, BorrowKind, Mutability};
1415
use rustc::mir::{UserTypeProjection};
1516
use rustc::mir::interpret::{GlobalId, ConstValue, sign_extend, AllocId, Pointer};
17+
use rustc::traits::{ObligationCause, PredicateObligation};
1618
use rustc::ty::{self, Region, TyCtxt, AdtDef, Ty, UserType, DefIdTree};
1719
use rustc::ty::{CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations};
1820
use rustc::ty::subst::{SubstsRef, Kind};
@@ -22,6 +24,7 @@ use rustc::hir::def::{CtorOf, Res, DefKind, CtorKind};
2224
use rustc::hir::pat_util::EnumerateAndAdjustIterator;
2325

2426
use rustc_data_structures::indexed_vec::Idx;
27+
use rustc_data_structures::fx::FxHashSet;
2528

2629
use std::cmp::Ordering;
2730
use std::fmt;
@@ -332,6 +335,7 @@ pub struct PatternContext<'a, 'tcx> {
332335
pub tables: &'a ty::TypeckTables<'tcx>,
333336
pub substs: SubstsRef<'tcx>,
334337
pub errors: Vec<PatternError>,
338+
include_lint_checks: bool,
335339
}
336340

337341
impl<'a, 'tcx> Pattern<'tcx> {
@@ -363,10 +367,16 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
363367
param_env: param_env_and_substs.param_env,
364368
tables,
365369
substs: param_env_and_substs.value,
366-
errors: vec![]
370+
errors: vec![],
371+
include_lint_checks: false,
367372
}
368373
}
369374

375+
pub fn include_lint_checks(&mut self) -> &mut Self {
376+
self.include_lint_checks = true;
377+
self
378+
}
379+
370380
pub fn lower_pattern(&mut self, pat: &'tcx hir::Pat) -> Pattern<'tcx> {
371381
// When implicit dereferences have been inserted in this pattern, the unadjusted lowered
372382
// pattern has the type that results *after* dereferencing. For example, in this code:
@@ -942,23 +952,94 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
942952

943953
/// Converts an evaluated constant to a pattern (if possible).
944954
/// This means aggregate values (like structs and enums) are converted
945-
/// to a pattern that matches the value (as if you'd compared via equality).
955+
/// to a pattern that matches the value (as if you'd compared via structural equality).
946956
fn const_to_pat(
947957
&self,
948958
instance: ty::Instance<'tcx>,
949959
cv: &'tcx ty::Const<'tcx>,
950960
id: hir::HirId,
951961
span: Span,
952962
) -> Pattern<'tcx> {
963+
// This method is just a warpper handling a validity check; the heavy lifting is
964+
// performed by the recursive const_to_pat_inner method, which is not meant to be
965+
// invoked except by this method.
966+
//
967+
// once indirect_structural_match is a full fledged error, this
968+
// level of indirection can be eliminated
969+
953970
debug!("const_to_pat: cv={:#?} id={:?}", cv, id);
954-
let adt_subpattern = |i, variant_opt| {
971+
debug!("const_to_pat: cv.ty={:?} span={:?}", cv.ty, span);
972+
973+
let mut saw_error = false;
974+
let inlined_const_as_pat = self.const_to_pat_inner(instance, cv, id, span, &mut saw_error);
975+
976+
if self.include_lint_checks && !saw_error {
977+
// If we were able to successfully convert the const to some pat, double-check
978+
// that the type of the const obeys `#[structural_match]` constraint.
979+
if let Some(adt_def) = search_for_adt_without_structural_match(self.tcx, cv.ty) {
980+
981+
let path = self.tcx.def_path_str(adt_def.did);
982+
let msg = format!(
983+
"to use a constant of type `{}` in a pattern, \
984+
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
985+
path,
986+
path,
987+
);
988+
989+
// before issuing lint, double-check there even *is* a
990+
// semantic PartialEq for us to dispatch to.
991+
//
992+
// (If there isn't, then we can safely issue a hard
993+
// error, because that's never worked, due to compiler
994+
// using PartialEq::eq in this scenario in the past.)
995+
996+
let ty_is_partial_eq: bool = {
997+
let partial_eq_trait_id = self.tcx.lang_items().eq_trait().unwrap();
998+
let obligation: PredicateObligation<'_> =
999+
self.tcx.predicate_for_trait_def(self.param_env,
1000+
ObligationCause::misc(span, id),
1001+
partial_eq_trait_id,
1002+
0,
1003+
cv.ty,
1004+
&[]);
1005+
self.tcx
1006+
.infer_ctxt()
1007+
.enter(|infcx| infcx.predicate_may_hold(&obligation))
1008+
};
1009+
1010+
if !ty_is_partial_eq {
1011+
// span_fatal avoids ICE from resolution of non-existent method (rare case).
1012+
self.tcx.sess.span_fatal(span, &msg);
1013+
} else {
1014+
self.tcx.lint_hir(lint::builtin::INDIRECT_STRUCTURAL_MATCH, id, span, &msg);
1015+
}
1016+
}
1017+
}
1018+
1019+
inlined_const_as_pat
1020+
}
1021+
1022+
/// Recursive helper for `const_to_pat`; invoke that (instead of calling this directly).
1023+
fn const_to_pat_inner(
1024+
&self,
1025+
instance: ty::Instance<'tcx>,
1026+
cv: &'tcx ty::Const<'tcx>,
1027+
id: hir::HirId,
1028+
span: Span,
1029+
// This tracks if we signal some hard error for a given const
1030+
// value, so that we will not subsequently issue an irrelevant
1031+
// lint for the same const value.
1032+
saw_const_match_error: &mut bool,
1033+
) -> Pattern<'tcx> {
1034+
1035+
let mut adt_subpattern = |i, variant_opt| {
9551036
let field = Field::new(i);
9561037
let val = crate::const_eval::const_field(
9571038
self.tcx, self.param_env, variant_opt, field, cv
9581039
);
959-
self.const_to_pat(instance, val, id, span)
1040+
self.const_to_pat_inner(instance, val, id, span, saw_const_match_error)
9601041
};
961-
let adt_subpatterns = |n, variant_opt| {
1042+
let mut adt_subpatterns = |n, variant_opt| {
9621043
(0..n).map(|i| {
9631044
let field = Field::new(i);
9641045
FieldPattern {
@@ -967,7 +1048,8 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
9671048
}
9681049
}).collect::<Vec<_>>()
9691050
};
970-
debug!("const_to_pat: cv.ty={:?} span={:?}", cv.ty, span);
1051+
1052+
9711053
let kind = match cv.ty.sty {
9721054
ty::Float(_) => {
9731055
self.tcx.lint_hir(
@@ -982,9 +1064,11 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
9821064
}
9831065
ty::Adt(adt_def, _) if adt_def.is_union() => {
9841066
// Matching on union fields is unsafe, we can't hide it in constants
1067+
*saw_const_match_error = true;
9851068
self.tcx.sess.span_err(span, "cannot use unions in constant patterns");
9861069
PatternKind::Wild
9871070
}
1071+
// keep old code until future-compat upgraded to errors.
9881072
ty::Adt(adt_def, _) if !self.tcx.has_attr(adt_def.did, sym::structural_match) => {
9891073
let path = self.tcx.def_path_str(adt_def.did);
9901074
let msg = format!(
@@ -993,9 +1077,11 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
9931077
path,
9941078
path,
9951079
);
1080+
*saw_const_match_error = true;
9961081
self.tcx.sess.span_err(span, &msg);
9971082
PatternKind::Wild
9981083
}
1084+
// keep old code until future-compat upgraded to errors.
9991085
ty::Ref(_, ty::TyS { sty: ty::Adt(adt_def, _), .. }, _)
10001086
if !self.tcx.has_attr(adt_def.did, sym::structural_match) => {
10011087
// HACK(estebank): Side-step ICE #53708, but anything other than erroring here
@@ -1007,6 +1093,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
10071093
path,
10081094
path,
10091095
);
1096+
*saw_const_match_error = true;
10101097
self.tcx.sess.span_err(span, &msg);
10111098
PatternKind::Wild
10121099
}
@@ -1058,6 +1145,96 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
10581145
}
10591146
}
10601147

1148+
fn search_for_adt_without_structural_match<'tcx>(tcx: TyCtxt<'tcx>,
1149+
ty: Ty<'tcx>)
1150+
-> Option<&'tcx AdtDef>
1151+
{
1152+
// Import here (not mod level), because `TypeFoldable::fold_with`
1153+
// conflicts with `PatternFoldable::fold_with`
1154+
use crate::rustc::ty::fold::TypeVisitor;
1155+
use crate::rustc::ty::TypeFoldable;
1156+
1157+
let mut search = Search { tcx, found: None, seen: FxHashSet::default() };
1158+
ty.visit_with(&mut search);
1159+
return search.found;
1160+
1161+
struct Search<'tcx> {
1162+
tcx: TyCtxt<'tcx>,
1163+
1164+
// records the first ADT we find without `#[structural_match`
1165+
found: Option<&'tcx AdtDef>,
1166+
1167+
// tracks ADT's previously encountered during search, so that
1168+
// we will not recur on them again.
1169+
seen: FxHashSet<&'tcx AdtDef>,
1170+
}
1171+
1172+
impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> {
1173+
fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
1174+
debug!("Search visiting ty: {:?}", ty);
1175+
1176+
let (adt_def, substs) = match ty.sty {
1177+
ty::Adt(adt_def, substs) => (adt_def, substs),
1178+
ty::RawPtr(..) => {
1179+
// `#[structural_match]` ignores substructure of
1180+
// `*const _`/`*mut _`, so skip super_visit_with
1181+
1182+
// (But still tell caller to continue search.)
1183+
return false;
1184+
}
1185+
ty::Array(_, n) if n.assert_usize(self.tcx) == Some(0) => {
1186+
// rust-lang/rust#62336: ignore type of contents
1187+
// for empty array.
1188+
return false;
1189+
}
1190+
_ => {
1191+
ty.super_visit_with(self);
1192+
return false;
1193+
}
1194+
};
1195+
1196+
if !self.tcx.has_attr(adt_def.did, sym::structural_match) {
1197+
self.found = Some(&adt_def);
1198+
debug!("Search found adt_def: {:?}", adt_def);
1199+
return true // Halt visiting!
1200+
}
1201+
1202+
if self.seen.contains(adt_def) {
1203+
debug!("Search already seen adt_def: {:?}", adt_def);
1204+
// let caller continue its search
1205+
return false;
1206+
}
1207+
1208+
self.seen.insert(adt_def);
1209+
1210+
// `#[structural_match]` does not care about the
1211+
// instantiation of the generics in an ADT (it
1212+
// instead looks directly at its fields outside
1213+
// this match), so we skip super_visit_with.
1214+
//
1215+
// (Must not recur on substs for `PhantomData<T>` cf
1216+
// rust-lang/rust#55028 and rust-lang/rust#55837; but also
1217+
// want to skip substs when only uses of generic are
1218+
// behind unsafe pointers `*const T`/`*mut T`.)
1219+
1220+
// even though we skip super_visit_with, we must recur on
1221+
// fields of ADT.
1222+
let tcx = self.tcx;
1223+
for field_ty in adt_def.all_fields().map(|field| field.ty(tcx, substs)) {
1224+
if field_ty.visit_with(self) {
1225+
// found an ADT without `#[structural_match]`; halt visiting!
1226+
assert!(self.found.is_some());
1227+
return true;
1228+
}
1229+
}
1230+
1231+
// Even though we do not want to recur on substs, we do
1232+
// want our caller to continue its own search.
1233+
false
1234+
}
1235+
}
1236+
}
1237+
10611238
impl UserAnnotatedTyHelpers<'tcx> for PatternContext<'_, 'tcx> {
10621239
fn tcx(&self) -> TyCtxt<'tcx> {
10631240
self.tcx

0 commit comments

Comments
 (0)