Skip to content

Commit aa64015

Browse files
authored
Rollup merge of rust-lang#65627 - varkor:const-generics-forbid-non-structural_match, r=petrochenkov
Forbid non-`structural_match` types in const generics Fixes rust-lang#60286.
2 parents 3f86775 + 2dda8ad commit aa64015

16 files changed

+230
-207
lines changed

src/librustc/ty/mod.rs

+125-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ pub use self::Variance::*;
44
pub use self::AssocItemContainer::*;
55
pub use self::BorrowKind::*;
66
pub use self::IntVarValue::*;
7-
pub use self::fold::TypeFoldable;
7+
pub use self::fold::{TypeFoldable, TypeVisitor};
88

99
use crate::hir::{map as hir_map, GlobMap, TraitMap};
1010
use crate::hir::Node;
@@ -50,7 +50,7 @@ use syntax::symbol::{kw, sym, Symbol};
5050
use syntax_pos::Span;
5151

5252
use smallvec;
53-
use rustc_data_structures::fx::FxIndexMap;
53+
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
5454
use rustc_data_structures::stable_hasher::{StableHasher, HashStable};
5555
use rustc_index::vec::{Idx, IndexVec};
5656

@@ -3393,6 +3393,129 @@ fn asyncness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::IsAsync {
33933393
fn_like.asyncness()
33943394
}
33953395

3396+
pub enum NonStructuralMatchTy<'tcx> {
3397+
Adt(&'tcx AdtDef),
3398+
Param,
3399+
}
3400+
3401+
/// This method traverses the structure of `ty`, trying to find an
3402+
/// instance of an ADT (i.e. struct or enum) that was declared without
3403+
/// the `#[structural_match]` attribute, or a generic type parameter
3404+
/// (which cannot be determined to be `structural_match`).
3405+
///
3406+
/// The "structure of a type" includes all components that would be
3407+
/// considered when doing a pattern match on a constant of that
3408+
/// type.
3409+
///
3410+
/// * This means this method descends into fields of structs/enums,
3411+
/// and also descends into the inner type `T` of `&T` and `&mut T`
3412+
///
3413+
/// * The traversal doesn't dereference unsafe pointers (`*const T`,
3414+
/// `*mut T`), and it does not visit the type arguments of an
3415+
/// instantiated generic like `PhantomData<T>`.
3416+
///
3417+
/// The reason we do this search is Rust currently require all ADTs
3418+
/// reachable from a constant's type to be annotated with
3419+
/// `#[structural_match]`, an attribute which essentially says that
3420+
/// the implementation of `PartialEq::eq` behaves *equivalently* to a
3421+
/// comparison against the unfolded structure.
3422+
///
3423+
/// For more background on why Rust has this requirement, and issues
3424+
/// that arose when the requirement was not enforced completely, see
3425+
/// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307.
3426+
pub fn search_for_structural_match_violation<'tcx>(
3427+
tcx: TyCtxt<'tcx>,
3428+
ty: Ty<'tcx>,
3429+
) -> Option<NonStructuralMatchTy<'tcx>> {
3430+
let mut search = Search { tcx, found: None, seen: FxHashSet::default() };
3431+
ty.visit_with(&mut search);
3432+
return search.found;
3433+
3434+
struct Search<'tcx> {
3435+
tcx: TyCtxt<'tcx>,
3436+
3437+
// Records the first ADT or type parameter we find without `#[structural_match`.
3438+
found: Option<NonStructuralMatchTy<'tcx>>,
3439+
3440+
// Tracks ADTs previously encountered during search, so that
3441+
// we will not recurse on them again.
3442+
seen: FxHashSet<hir::def_id::DefId>,
3443+
}
3444+
3445+
impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> {
3446+
fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
3447+
debug!("Search visiting ty: {:?}", ty);
3448+
3449+
let (adt_def, substs) = match ty.kind {
3450+
ty::Adt(adt_def, substs) => (adt_def, substs),
3451+
ty::Param(_) => {
3452+
self.found = Some(NonStructuralMatchTy::Param);
3453+
return true; // Stop visiting.
3454+
}
3455+
ty::RawPtr(..) => {
3456+
// `#[structural_match]` ignores substructure of
3457+
// `*const _`/`*mut _`, so skip super_visit_with
3458+
//
3459+
// (But still tell caller to continue search.)
3460+
return false;
3461+
}
3462+
ty::FnDef(..) | ty::FnPtr(..) => {
3463+
// types of formals and return in `fn(_) -> _` are also irrelevant
3464+
//
3465+
// (But still tell caller to continue search.)
3466+
return false;
3467+
}
3468+
ty::Array(_, n) if n.try_eval_usize(self.tcx, ty::ParamEnv::reveal_all()) == Some(0)
3469+
=> {
3470+
// rust-lang/rust#62336: ignore type of contents
3471+
// for empty array.
3472+
return false;
3473+
}
3474+
_ => {
3475+
ty.super_visit_with(self);
3476+
return false;
3477+
}
3478+
};
3479+
3480+
if !self.tcx.has_attr(adt_def.did, sym::structural_match) {
3481+
self.found = Some(NonStructuralMatchTy::Adt(&adt_def));
3482+
debug!("Search found adt_def: {:?}", adt_def);
3483+
return true; // Stop visiting.
3484+
}
3485+
3486+
if !self.seen.insert(adt_def.did) {
3487+
debug!("Search already seen adt_def: {:?}", adt_def);
3488+
// let caller continue its search
3489+
return false;
3490+
}
3491+
3492+
// `#[structural_match]` does not care about the
3493+
// instantiation of the generics in an ADT (it
3494+
// instead looks directly at its fields outside
3495+
// this match), so we skip super_visit_with.
3496+
//
3497+
// (Must not recur on substs for `PhantomData<T>` cf
3498+
// rust-lang/rust#55028 and rust-lang/rust#55837; but also
3499+
// want to skip substs when only uses of generic are
3500+
// behind unsafe pointers `*const T`/`*mut T`.)
3501+
3502+
// even though we skip super_visit_with, we must recur on
3503+
// fields of ADT.
3504+
let tcx = self.tcx;
3505+
for field_ty in adt_def.all_fields().map(|field| field.ty(tcx, substs)) {
3506+
if field_ty.visit_with(self) {
3507+
// found an ADT without `#[structural_match]`; halt visiting!
3508+
assert!(self.found.is_some());
3509+
return true;
3510+
}
3511+
}
3512+
3513+
// Even though we do not want to recur on substs, we do
3514+
// want our caller to continue its own search.
3515+
false
3516+
}
3517+
}
3518+
}
33963519

33973520
pub fn provide(providers: &mut ty::query::Providers<'_>) {
33983521
context::provide(providers);

src/librustc/ty/relate.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -557,10 +557,9 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>(
557557
x.val
558558
};
559559

560-
// Currently, the values that can be unified are those that
561-
// implement both `PartialEq` and `Eq`, corresponding to
562-
// `structural_match` types.
563-
// FIXME(const_generics): check for `structural_match` synthetic attribute.
560+
// Currently, the values that can be unified are primitive types,
561+
// and those that derive both `PartialEq` and `Eq`, corresponding
562+
// to `structural_match` types.
564563
let new_const_val = match (eagerly_eval(a), eagerly_eval(b)) {
565564
(ConstValue::Infer(_), _) | (_, ConstValue::Infer(_)) => {
566565
// The caller should handle these cases!

src/librustc_mir/hair/pattern/mod.rs

+15-129
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ use rustc::hir::pat_util::EnumerateAndAdjustIterator;
2525
use rustc::hir::ptr::P;
2626

2727
use rustc_index::vec::Idx;
28-
use rustc_data_structures::fx::FxHashSet;
2928

3029
use std::cmp::Ordering;
3130
use std::fmt;
@@ -1000,15 +999,21 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
1000999
if self.include_lint_checks && !saw_error {
10011000
// If we were able to successfully convert the const to some pat, double-check
10021001
// that the type of the const obeys `#[structural_match]` constraint.
1003-
if let Some(adt_def) = search_for_adt_without_structural_match(self.tcx, cv.ty) {
1004-
1005-
let path = self.tcx.def_path_str(adt_def.did);
1006-
let msg = format!(
1007-
"to use a constant of type `{}` in a pattern, \
1008-
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
1009-
path,
1010-
path,
1011-
);
1002+
if let Some(non_sm_ty) = ty::search_for_structural_match_violation(self.tcx, cv.ty) {
1003+
let msg = match non_sm_ty {
1004+
ty::NonStructuralMatchTy::Adt(adt_def) => {
1005+
let path = self.tcx.def_path_str(adt_def.did);
1006+
format!(
1007+
"to use a constant of type `{}` in a pattern, \
1008+
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
1009+
path,
1010+
path,
1011+
)
1012+
}
1013+
ty::NonStructuralMatchTy::Param => {
1014+
bug!("use of constant whose type is a parameter inside a pattern");
1015+
}
1016+
};
10121017

10131018
// before issuing lint, double-check there even *is* a
10141019
// semantic PartialEq for us to dispatch to.
@@ -1169,125 +1174,6 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
11691174
}
11701175
}
11711176

1172-
/// This method traverses the structure of `ty`, trying to find an
1173-
/// instance of an ADT (i.e. struct or enum) that was declared without
1174-
/// the `#[structural_match]` attribute.
1175-
///
1176-
/// The "structure of a type" includes all components that would be
1177-
/// considered when doing a pattern match on a constant of that
1178-
/// type.
1179-
///
1180-
/// * This means this method descends into fields of structs/enums,
1181-
/// and also descends into the inner type `T` of `&T` and `&mut T`
1182-
///
1183-
/// * The traversal doesn't dereference unsafe pointers (`*const T`,
1184-
/// `*mut T`), and it does not visit the type arguments of an
1185-
/// instantiated generic like `PhantomData<T>`.
1186-
///
1187-
/// The reason we do this search is Rust currently require all ADT's
1188-
/// reachable from a constant's type to be annotated with
1189-
/// `#[structural_match]`, an attribute which essentially says that
1190-
/// the implementation of `PartialEq::eq` behaves *equivalently* to a
1191-
/// comparison against the unfolded structure.
1192-
///
1193-
/// For more background on why Rust has this requirement, and issues
1194-
/// that arose when the requirement was not enforced completely, see
1195-
/// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307.
1196-
fn search_for_adt_without_structural_match<'tcx>(tcx: TyCtxt<'tcx>,
1197-
ty: Ty<'tcx>)
1198-
-> Option<&'tcx AdtDef>
1199-
{
1200-
// Import here (not mod level), because `TypeFoldable::fold_with`
1201-
// conflicts with `PatternFoldable::fold_with`
1202-
use crate::rustc::ty::fold::TypeVisitor;
1203-
use crate::rustc::ty::TypeFoldable;
1204-
1205-
let mut search = Search { tcx, found: None, seen: FxHashSet::default() };
1206-
ty.visit_with(&mut search);
1207-
return search.found;
1208-
1209-
struct Search<'tcx> {
1210-
tcx: TyCtxt<'tcx>,
1211-
1212-
// records the first ADT we find without `#[structural_match`
1213-
found: Option<&'tcx AdtDef>,
1214-
1215-
// tracks ADT's previously encountered during search, so that
1216-
// we will not recur on them again.
1217-
seen: FxHashSet<hir::def_id::DefId>,
1218-
}
1219-
1220-
impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> {
1221-
fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
1222-
debug!("Search visiting ty: {:?}", ty);
1223-
1224-
let (adt_def, substs) = match ty.kind {
1225-
ty::Adt(adt_def, substs) => (adt_def, substs),
1226-
ty::RawPtr(..) => {
1227-
// `#[structural_match]` ignores substructure of
1228-
// `*const _`/`*mut _`, so skip super_visit_with
1229-
//
1230-
// (But still tell caller to continue search.)
1231-
return false;
1232-
}
1233-
ty::FnDef(..) | ty::FnPtr(..) => {
1234-
// types of formals and return in `fn(_) -> _` are also irrelevant
1235-
//
1236-
// (But still tell caller to continue search.)
1237-
return false;
1238-
}
1239-
ty::Array(_, n) if n.try_eval_usize(self.tcx, ty::ParamEnv::reveal_all()) == Some(0)
1240-
=> {
1241-
// rust-lang/rust#62336: ignore type of contents
1242-
// for empty array.
1243-
return false;
1244-
}
1245-
_ => {
1246-
ty.super_visit_with(self);
1247-
return false;
1248-
}
1249-
};
1250-
1251-
if !self.tcx.has_attr(adt_def.did, sym::structural_match) {
1252-
self.found = Some(&adt_def);
1253-
debug!("Search found adt_def: {:?}", adt_def);
1254-
return true // Halt visiting!
1255-
}
1256-
1257-
if !self.seen.insert(adt_def.did) {
1258-
debug!("Search already seen adt_def: {:?}", adt_def);
1259-
// let caller continue its search
1260-
return false;
1261-
}
1262-
1263-
// `#[structural_match]` does not care about the
1264-
// instantiation of the generics in an ADT (it
1265-
// instead looks directly at its fields outside
1266-
// this match), so we skip super_visit_with.
1267-
//
1268-
// (Must not recur on substs for `PhantomData<T>` cf
1269-
// rust-lang/rust#55028 and rust-lang/rust#55837; but also
1270-
// want to skip substs when only uses of generic are
1271-
// behind unsafe pointers `*const T`/`*mut T`.)
1272-
1273-
// even though we skip super_visit_with, we must recur on
1274-
// fields of ADT.
1275-
let tcx = self.tcx;
1276-
for field_ty in adt_def.all_fields().map(|field| field.ty(tcx, substs)) {
1277-
if field_ty.visit_with(self) {
1278-
// found an ADT without `#[structural_match]`; halt visiting!
1279-
assert!(self.found.is_some());
1280-
return true;
1281-
}
1282-
}
1283-
1284-
// Even though we do not want to recur on substs, we do
1285-
// want our caller to continue its own search.
1286-
false
1287-
}
1288-
}
1289-
}
1290-
12911177
impl UserAnnotatedTyHelpers<'tcx> for PatCtxt<'_, 'tcx> {
12921178
fn tcx(&self) -> TyCtxt<'tcx> {
12931179
self.tcx

src/librustc_resolve/diagnostics.rs

-10
Original file line numberDiff line numberDiff line change
@@ -367,16 +367,6 @@ impl<'a> Resolver<'a> {
367367
span, "`Self` in type parameter default".to_string());
368368
err
369369
}
370-
ResolutionError::ConstParamDependentOnTypeParam => {
371-
let mut err = struct_span_err!(
372-
self.session,
373-
span,
374-
E0671,
375-
"const parameters cannot depend on type parameters"
376-
);
377-
err.span_label(span, format!("const parameter depends on type parameter"));
378-
err
379-
}
380370
}
381371
}
382372

src/librustc_resolve/error_codes.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -1880,13 +1880,14 @@ fn main() {
18801880
"##,
18811881

18821882
E0671: r##"
1883+
#### Note: this error code is no longer emitted by the compiler.
1884+
18831885
Const parameters cannot depend on type parameters.
18841886
The following is therefore invalid:
1885-
```compile_fail,E0671
1887+
```compile_fail,E0741
18861888
#![feature(const_generics)]
18871889
1888-
fn const_id<T, const N: T>() -> T { // error: const parameter
1889-
// depends on type parameter
1890+
fn const_id<T, const N: T>() -> T { // error
18901891
N
18911892
}
18921893
```

0 commit comments

Comments
 (0)