Skip to content

Commit b025f26

Browse files
authored
Rollup merge of rust-lang#57501 - petrochenkov:highvar, r=alexreg
High priority resolutions for associated variants In rust-lang#56225 variants were assigned lowest priority during name resolution to avoid crater run and potential breakage. This PR changes the rules to give variants highest priority instead. Some motivation: - If variants (and their constructors) are treated as associated items, then they are obviously *inherent* associated items since they don't come from traits. - Inherent associated items have higher priority during resolution than associated items from traits. - The reason is that there is a way to disambiguate in favor of trait items (`<Type as Trait>::Ambiguous`), but there's no way to disambiguate in favor of inherent items, so they became unusable in case of ambiguities if they have low priority. - It's technically problematic to fallback from associated types to anything until lazy normalization (?) is implemented. Crater found some regressions from this change, but they are all in type positions, e.g. ```rust fn f() -> Self::Ambiguos { ... } // Variant `Ambiguous` or associated type `Ambiguous`? ``` , so variants are not usable there right now, but they may become usable in the future if rust-lang/rfcs#2593 is accepted. This PR keeps code like this successfully resolving, but introduces a future-compatibility lint `ambiguous_associated_items` that recommends rewriting it as `<Self as Trait>::Ambiguous`.
2 parents dd545ee + 01d0ae9 commit b025f26

11 files changed

+225
-103
lines changed

src/librustc/lint/builtin.rs

+7
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,12 @@ declare_lint! {
368368
report_in_external_macro: true
369369
}
370370

371+
declare_lint! {
372+
pub AMBIGUOUS_ASSOCIATED_ITEMS,
373+
Warn,
374+
"ambiguous associated items"
375+
}
376+
371377
/// Does nothing as a lint pass, but registers some `Lint`s
372378
/// that are used by other parts of the compiler.
373379
#[derive(Copy, Clone)]
@@ -433,6 +439,7 @@ impl LintPass for HardwiredLints {
433439
parser::QUESTION_MARK_MACRO_SEP,
434440
parser::ILL_FORMED_ATTRIBUTE_INPUT,
435441
DEPRECATED_IN_FUTURE,
442+
AMBIGUOUS_ASSOCIATED_ITEMS,
436443
)
437444
}
438445
}

src/librustc_lint/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,11 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
341341
reference: "issue #57571 <https://github.com/rust-lang/rust/issues/57571>",
342342
edition: None,
343343
},
344+
FutureIncompatibleInfo {
345+
id: LintId::of(AMBIGUOUS_ASSOCIATED_ITEMS),
346+
reference: "issue #57644 <https://github.com/rust-lang/rust/issues/57644>",
347+
edition: None,
348+
},
344349
]);
345350

346351
// Register renamed and removed lints.

src/librustc_typeck/astconv.rs

+96-66
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use hir::HirVec;
1010
use lint;
1111
use middle::resolve_lifetime as rl;
1212
use namespace::Namespace;
13+
use rustc::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS;
1314
use rustc::traits;
1415
use rustc::ty::{self, Ty, TyCtxt, ToPredicate, TypeFoldable};
1516
use rustc::ty::{GenericParamDef, GenericParamDefKind};
@@ -1278,29 +1279,50 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
12781279
}
12791280

12801281
// Create a type from a path to an associated type.
1281-
// For a path `A::B::C::D`, `ty` and `ty_path_def` are the type and def for `A::B::C`
1282+
// For a path `A::B::C::D`, `qself_ty` and `qself_def` are the type and def for `A::B::C`
12821283
// and item_segment is the path segment for `D`. We return a type and a def for
12831284
// the whole path.
1284-
// Will fail except for `T::A` and `Self::A`; i.e., if `ty`/`ty_path_def` are not a type
1285+
// Will fail except for `T::A` and `Self::A`; i.e., if `qself_ty`/`qself_def` are not a type
12851286
// parameter or `Self`.
1286-
pub fn associated_path_def_to_ty(&self,
1287-
ref_id: ast::NodeId,
1288-
span: Span,
1289-
ty: Ty<'tcx>,
1290-
ty_path_def: Def,
1291-
item_segment: &hir::PathSegment)
1292-
-> (Ty<'tcx>, Def)
1293-
{
1287+
pub fn associated_path_to_ty(
1288+
&self,
1289+
ref_id: ast::NodeId,
1290+
span: Span,
1291+
qself_ty: Ty<'tcx>,
1292+
qself_def: Def,
1293+
assoc_segment: &hir::PathSegment,
1294+
permit_variants: bool,
1295+
) -> (Ty<'tcx>, Def) {
12941296
let tcx = self.tcx();
1295-
let assoc_name = item_segment.ident;
1297+
let assoc_ident = assoc_segment.ident;
12961298

1297-
debug!("associated_path_def_to_ty: {:?}::{}", ty, assoc_name);
1299+
debug!("associated_path_to_ty: {:?}::{}", qself_ty, assoc_ident);
12981300

1299-
self.prohibit_generics(slice::from_ref(item_segment));
1301+
self.prohibit_generics(slice::from_ref(assoc_segment));
1302+
1303+
// Check if we have an enum variant.
1304+
let mut variant_resolution = None;
1305+
if let ty::Adt(adt_def, _) = qself_ty.sty {
1306+
if adt_def.is_enum() {
1307+
let variant_def = adt_def.variants.iter().find(|vd| {
1308+
tcx.hygienic_eq(assoc_ident, vd.ident, adt_def.did)
1309+
});
1310+
if let Some(variant_def) = variant_def {
1311+
let def = Def::Variant(variant_def.did);
1312+
if permit_variants {
1313+
check_type_alias_enum_variants_enabled(tcx, span);
1314+
tcx.check_stability(variant_def.did, Some(ref_id), span);
1315+
return (qself_ty, def);
1316+
} else {
1317+
variant_resolution = Some(def);
1318+
}
1319+
}
1320+
}
1321+
}
13001322

13011323
// Find the type of the associated item, and the trait where the associated
13021324
// item is declared.
1303-
let bound = match (&ty.sty, ty_path_def) {
1325+
let bound = match (&qself_ty.sty, qself_def) {
13041326
(_, Def::SelfTy(Some(_), Some(impl_def_id))) => {
13051327
// `Self` in an impl of a trait -- we have a concrete self type and a
13061328
// trait reference.
@@ -1313,77 +1335,61 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
13131335
};
13141336

13151337
let candidates = traits::supertraits(tcx, ty::Binder::bind(trait_ref))
1316-
.filter(|r| self.trait_defines_associated_type_named(r.def_id(), assoc_name));
1338+
.filter(|r| self.trait_defines_associated_type_named(r.def_id(), assoc_ident));
13171339

1318-
match self.one_bound_for_assoc_type(candidates, "Self", assoc_name, span) {
1340+
match self.one_bound_for_assoc_type(candidates, "Self", assoc_ident, span) {
13191341
Ok(bound) => bound,
13201342
Err(ErrorReported) => return (tcx.types.err, Def::Err),
13211343
}
13221344
}
13231345
(&ty::Param(_), Def::SelfTy(Some(param_did), None)) |
13241346
(&ty::Param(_), Def::TyParam(param_did)) => {
1325-
match self.find_bound_for_assoc_item(param_did, assoc_name, span) {
1347+
match self.find_bound_for_assoc_item(param_did, assoc_ident, span) {
13261348
Ok(bound) => bound,
13271349
Err(ErrorReported) => return (tcx.types.err, Def::Err),
13281350
}
13291351
}
1330-
(&ty::Adt(adt_def, _substs), Def::Enum(_did)) => {
1331-
let ty_str = ty.to_string();
1332-
// Incorrect enum variant.
1333-
let mut err = tcx.sess.struct_span_err(
1334-
span,
1335-
&format!("no variant `{}` on enum `{}`", &assoc_name.as_str(), ty_str),
1336-
);
1337-
// Check if it was a typo.
1338-
let input = adt_def.variants.iter().map(|variant| &variant.ident.name);
1339-
if let Some(suggested_name) = find_best_match_for_name(
1340-
input,
1341-
&assoc_name.as_str(),
1342-
None,
1343-
) {
1344-
err.span_suggestion_with_applicability(
1352+
_ => {
1353+
if variant_resolution.is_some() {
1354+
// Variant in type position
1355+
let msg = format!("expected type, found variant `{}`", assoc_ident);
1356+
tcx.sess.span_err(span, &msg);
1357+
} else if qself_ty.is_enum() {
1358+
// Report as incorrect enum variant rather than ambiguous type.
1359+
let mut err = tcx.sess.struct_span_err(
13451360
span,
1346-
"did you mean",
1347-
format!("{}::{}", ty_str, suggested_name.to_string()),
1348-
Applicability::MaybeIncorrect,
1361+
&format!("no variant `{}` on enum `{}`", &assoc_ident.as_str(), qself_ty),
13491362
);
1350-
} else {
1351-
err.span_label(span, "unknown variant");
1352-
}
1353-
err.emit();
1354-
return (tcx.types.err, Def::Err);
1355-
}
1356-
_ => {
1357-
// Check if we have an enum variant.
1358-
match ty.sty {
1359-
ty::Adt(adt_def, _) if adt_def.is_enum() => {
1360-
let variant_def = adt_def.variants.iter().find(|vd| {
1361-
tcx.hygienic_eq(assoc_name, vd.ident, adt_def.did)
1362-
});
1363-
if let Some(variant_def) = variant_def {
1364-
check_type_alias_enum_variants_enabled(tcx, span);
1365-
1366-
let def = Def::Variant(variant_def.did);
1367-
tcx.check_stability(def.def_id(), Some(ref_id), span);
1368-
return (ty, def);
1369-
}
1370-
},
1371-
_ => (),
1372-
}
1373-
1374-
// Don't print `TyErr` to the user.
1375-
if !ty.references_error() {
1363+
// Check if it was a typo.
1364+
let adt_def = qself_ty.ty_adt_def().expect("enum is not an ADT");
1365+
if let Some(suggested_name) = find_best_match_for_name(
1366+
adt_def.variants.iter().map(|variant| &variant.ident.name),
1367+
&assoc_ident.as_str(),
1368+
None,
1369+
) {
1370+
err.span_suggestion_with_applicability(
1371+
span,
1372+
"did you mean",
1373+
format!("{}::{}", qself_ty, suggested_name),
1374+
Applicability::MaybeIncorrect,
1375+
);
1376+
} else {
1377+
err.span_label(span, "unknown variant");
1378+
}
1379+
err.emit();
1380+
} else if !qself_ty.references_error() {
1381+
// Don't print `TyErr` to the user.
13761382
self.report_ambiguous_associated_type(span,
1377-
&ty.to_string(),
1383+
&qself_ty.to_string(),
13781384
"Trait",
1379-
&assoc_name.as_str());
1385+
&assoc_ident.as_str());
13801386
}
13811387
return (tcx.types.err, Def::Err);
13821388
}
13831389
};
13841390

13851391
let trait_did = bound.def_id();
1386-
let (assoc_ident, def_scope) = tcx.adjust_ident(assoc_name, trait_did, ref_id);
1392+
let (assoc_ident, def_scope) = tcx.adjust_ident(assoc_ident, trait_did, ref_id);
13871393
let item = tcx.associated_items(trait_did).find(|i| {
13881394
Namespace::from(i.kind) == Namespace::Type &&
13891395
i.ident.modern() == assoc_ident
@@ -1394,11 +1400,35 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
13941400

13951401
let def = Def::AssociatedTy(item.def_id);
13961402
if !item.vis.is_accessible_from(def_scope, tcx) {
1397-
let msg = format!("{} `{}` is private", def.kind_name(), assoc_name);
1403+
let msg = format!("{} `{}` is private", def.kind_name(), assoc_ident);
13981404
tcx.sess.span_err(span, &msg);
13991405
}
14001406
tcx.check_stability(item.def_id, Some(ref_id), span);
14011407

1408+
if let Some(variant_def) = variant_resolution {
1409+
let mut err = tcx.struct_span_lint_node(
1410+
AMBIGUOUS_ASSOCIATED_ITEMS,
1411+
ref_id,
1412+
span,
1413+
"ambiguous associated item",
1414+
);
1415+
1416+
let mut could_refer_to = |def: Def, also| {
1417+
let note_msg = format!("`{}` could{} refer to {} defined here",
1418+
assoc_ident, also, def.kind_name());
1419+
err.span_note(tcx.def_span(def.def_id()), &note_msg);
1420+
};
1421+
could_refer_to(variant_def, "");
1422+
could_refer_to(def, " also");
1423+
1424+
err.span_suggestion_with_applicability(
1425+
span,
1426+
"use fully-qualified syntax",
1427+
format!("<{} as {}>::{}", qself_ty, "Trait", assoc_ident),
1428+
Applicability::HasPlaceholders,
1429+
).emit();
1430+
}
1431+
14021432
(ty, def)
14031433
}
14041434

@@ -1773,7 +1803,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
17731803
} else {
17741804
Def::Err
17751805
};
1776-
self.associated_path_def_to_ty(ast_ty.id, ast_ty.span, ty, def, segment).0
1806+
self.associated_path_to_ty(ast_ty.id, ast_ty.span, ty, def, segment, false).0
17771807
}
17781808
hir::TyKind::Array(ref ty, ref length) => {
17791809
let length_def_id = tcx.hir().local_def_id(length.id);

src/librustc_typeck/check/method/mod.rs

+26-35
Original file line numberDiff line numberDiff line change
@@ -408,45 +408,36 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
408408

409409
let tcx = self.tcx;
410410

411-
let mode = probe::Mode::Path;
412-
match self.probe_for_name(span, mode, method_name, IsSuggestion(false),
413-
self_ty, expr_id, ProbeScope::TraitsInScope) {
414-
Ok(pick) => {
415-
debug!("resolve_ufcs: pick={:?}", pick);
416-
if let Some(import_id) = pick.import_id {
417-
let import_def_id = tcx.hir().local_def_id(import_id);
418-
debug!("resolve_ufcs: used_trait_import: {:?}", import_def_id);
419-
Lrc::get_mut(&mut self.tables.borrow_mut().used_trait_imports)
420-
.unwrap().insert(import_def_id);
411+
// Check if we have an enum variant.
412+
if let ty::Adt(adt_def, _) = self_ty.sty {
413+
if adt_def.is_enum() {
414+
let variant_def = adt_def.variants.iter().find(|vd| {
415+
tcx.hygienic_eq(method_name, vd.ident, adt_def.did)
416+
});
417+
if let Some(variant_def) = variant_def {
418+
check_type_alias_enum_variants_enabled(tcx, span);
419+
420+
let def = Def::VariantCtor(variant_def.did, variant_def.ctor_kind);
421+
tcx.check_stability(def.def_id(), Some(expr_id), span);
422+
return Ok(def);
421423
}
422-
423-
let def = pick.item.def();
424-
debug!("resolve_ufcs: def={:?}", def);
425-
tcx.check_stability(def.def_id(), Some(expr_id), span);
426-
427-
Ok(def)
428424
}
429-
Err(err) => {
430-
// Check if we have an enum variant.
431-
match self_ty.sty {
432-
ty::Adt(adt_def, _) if adt_def.is_enum() => {
433-
let variant_def = adt_def.variants.iter().find(|vd| {
434-
tcx.hygienic_eq(method_name, vd.ident, adt_def.did)
435-
});
436-
if let Some(variant_def) = variant_def {
437-
check_type_alias_enum_variants_enabled(tcx, span);
438-
439-
let def = Def::VariantCtor(variant_def.did, variant_def.ctor_kind);
440-
tcx.check_stability(def.def_id(), Some(expr_id), span);
441-
return Ok(def);
442-
}
443-
},
444-
_ => (),
445-
}
425+
}
446426

447-
Err(err)
448-
}
427+
let pick = self.probe_for_name(span, probe::Mode::Path, method_name, IsSuggestion(false),
428+
self_ty, expr_id, ProbeScope::TraitsInScope)?;
429+
debug!("resolve_ufcs: pick={:?}", pick);
430+
if let Some(import_id) = pick.import_id {
431+
let import_def_id = tcx.hir().local_def_id(import_id);
432+
debug!("resolve_ufcs: used_trait_import: {:?}", import_def_id);
433+
Lrc::get_mut(&mut self.tables.borrow_mut().used_trait_imports)
434+
.unwrap().insert(import_def_id);
449435
}
436+
437+
let def = pick.item.def();
438+
debug!("resolve_ufcs: def={:?}", def);
439+
tcx.check_stability(def.def_id(), Some(expr_id), span);
440+
Ok(def)
450441
}
451442

452443
/// Find item with name `item_name` defined in impl/trait `def_id`

src/librustc_typeck/check/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -4724,8 +4724,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
47244724
} else {
47254725
Def::Err
47264726
};
4727-
let (ty, def) = AstConv::associated_path_def_to_ty(self, node_id, path_span,
4728-
ty, def, segment);
4727+
let (ty, def) = AstConv::associated_path_to_ty(self, node_id, path_span,
4728+
ty, def, segment, true);
47294729

47304730
// Write back the new resolution.
47314731
let hir_id = self.tcx.hir().node_to_hir_id(node_id);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#![feature(type_alias_enum_variants)]
2+
3+
enum E {
4+
V(u8)
5+
}
6+
7+
impl E {
8+
fn V() {}
9+
}
10+
11+
fn main() {
12+
<E>::V(); //~ ERROR this function takes 1 parameter but 0 parameters were supplied
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0061]: this function takes 1 parameter but 0 parameters were supplied
2+
--> $DIR/type-alias-enum-variants-priority-2.rs:12:5
3+
|
4+
LL | V(u8)
5+
| ----- defined here
6+
...
7+
LL | <E>::V(); //~ ERROR this function takes 1 parameter but 0 parameters were supplied
8+
| ^^^^^^^^ expected 1 parameter
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0061`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#![feature(type_alias_enum_variants)]
2+
3+
enum E {
4+
V
5+
}
6+
7+
fn check() -> <E>::V {}
8+
//~^ ERROR expected type, found variant `V`
9+
10+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: expected type, found variant `V`
2+
--> $DIR/type-alias-enum-variants-priority-3.rs:7:15
3+
|
4+
LL | fn check() -> <E>::V {}
5+
| ^^^^^^
6+
7+
error: aborting due to previous error
8+

0 commit comments

Comments
 (0)