Skip to content

Commit ed1cffd

Browse files
committed
Auto merge of #44818 - petrochenkov:astymac2, r=jseyfried
Improve resolution of associated types in declarative macros 2.0 Make various identifier comparisons for associated types (and sometimes other associated items) hygienic. Now declarative macros 2.0 can use `Self::AssocTy`, `TyParam::AssocTy`, `Trait<AssocTy = u8>` where `AssocTy` is an associated type of a trait `Trait` visible from the macro. Also, `Trait` can now be implemented inside the macro and specialization should work properly (fixes #40847 (comment)). r? @jseyfried or @eddyb
2 parents b915820 + 2d9161d commit ed1cffd

File tree

11 files changed

+182
-37
lines changed

11 files changed

+182
-37
lines changed

src/librustc/traits/project.rs

+7-8
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ use infer::{InferCtxt, InferOk};
2929
use infer::type_variable::TypeVariableOrigin;
3030
use middle::const_val::ConstVal;
3131
use rustc_data_structures::snapshot_map::{Snapshot, SnapshotMap};
32-
use syntax::ast;
3332
use syntax::symbol::Symbol;
3433
use ty::subst::{Subst, Substs};
3534
use ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt};
@@ -1044,10 +1043,9 @@ fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>(
10441043
// In either case, we handle this by not adding a
10451044
// candidate for an impl if it contains a `default`
10461045
// type.
1047-
let item_name = selcx.tcx().associated_item(obligation.predicate.item_def_id).name;
10481046
let node_item = assoc_ty_def(selcx,
10491047
impl_data.impl_def_id,
1050-
item_name);
1048+
obligation.predicate.item_def_id);
10511049

10521050
let is_default = if node_item.node.is_from_trait() {
10531051
// If true, the impl inherited a `type Foo = Bar`
@@ -1441,8 +1439,7 @@ fn confirm_impl_candidate<'cx, 'gcx, 'tcx>(
14411439

14421440
let tcx = selcx.tcx();
14431441
let param_env = obligation.param_env;
1444-
let assoc_ty = assoc_ty_def(selcx, impl_def_id,
1445-
tcx.associated_item(obligation.predicate.item_def_id).name);
1442+
let assoc_ty = assoc_ty_def(selcx, impl_def_id, obligation.predicate.item_def_id);
14461443

14471444
let ty = if !assoc_ty.item.defaultness.has_value() {
14481445
// This means that the impl is missing a definition for the
@@ -1471,10 +1468,11 @@ fn confirm_impl_candidate<'cx, 'gcx, 'tcx>(
14711468
fn assoc_ty_def<'cx, 'gcx, 'tcx>(
14721469
selcx: &SelectionContext<'cx, 'gcx, 'tcx>,
14731470
impl_def_id: DefId,
1474-
assoc_ty_name: ast::Name)
1471+
assoc_ty_def_id: DefId)
14751472
-> specialization_graph::NodeItem<ty::AssociatedItem>
14761473
{
14771474
let tcx = selcx.tcx();
1475+
let assoc_ty_name = tcx.associated_item(assoc_ty_def_id).name;
14781476
let trait_def_id = tcx.impl_trait_ref(impl_def_id).unwrap().def_id;
14791477
let trait_def = tcx.trait_def(trait_def_id);
14801478

@@ -1486,7 +1484,8 @@ fn assoc_ty_def<'cx, 'gcx, 'tcx>(
14861484
// cycle error if the specialization graph is currently being built.
14871485
let impl_node = specialization_graph::Node::Impl(impl_def_id);
14881486
for item in impl_node.items(tcx) {
1489-
if item.kind == ty::AssociatedKind::Type && item.name == assoc_ty_name {
1487+
if item.kind == ty::AssociatedKind::Type &&
1488+
tcx.hygienic_eq(item.name, assoc_ty_name, trait_def_id) {
14901489
return specialization_graph::NodeItem {
14911490
node: specialization_graph::Node::Impl(impl_def_id),
14921491
item,
@@ -1496,7 +1495,7 @@ fn assoc_ty_def<'cx, 'gcx, 'tcx>(
14961495

14971496
if let Some(assoc_item) = trait_def
14981497
.ancestors(tcx, impl_def_id)
1499-
.defs(tcx, assoc_ty_name, ty::AssociatedKind::Type)
1498+
.defs(tcx, assoc_ty_name, ty::AssociatedKind::Type, trait_def_id)
15001499
.next() {
15011500
assoc_item
15021501
} else {

src/librustc/traits/specialize/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ pub fn find_associated_item<'a, 'tcx>(
125125
let trait_def = tcx.trait_def(trait_def_id);
126126

127127
let ancestors = trait_def.ancestors(tcx, impl_data.impl_def_id);
128-
match ancestors.defs(tcx, item.name, item.kind).next() {
128+
match ancestors.defs(tcx, item.name, item.kind, trait_def_id).next() {
129129
Some(node_item) => {
130130
let substs = tcx.infer_ctxt().enter(|infcx| {
131131
let param_env = ty::ParamEnv::empty(Reveal::All);

src/librustc/traits/specialize/specialization_graph.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -346,11 +346,14 @@ impl<'a, 'gcx, 'tcx> Ancestors {
346346
/// Search the items from the given ancestors, returning each definition
347347
/// with the given name and the given kind.
348348
#[inline] // FIXME(#35870) Avoid closures being unexported due to impl Trait.
349-
pub fn defs(self, tcx: TyCtxt<'a, 'gcx, 'tcx>, name: Name, kind: ty::AssociatedKind)
349+
pub fn defs(self, tcx: TyCtxt<'a, 'gcx, 'tcx>, trait_item_name: Name,
350+
trait_item_kind: ty::AssociatedKind, trait_def_id: DefId)
350351
-> impl Iterator<Item = NodeItem<ty::AssociatedItem>> + 'a {
351352
self.flat_map(move |node| {
352-
node.items(tcx).filter(move |item| item.kind == kind && item.name == name)
353-
.map(move |item| NodeItem { node: node, item: item })
353+
node.items(tcx).filter(move |impl_item| {
354+
impl_item.kind == trait_item_kind &&
355+
tcx.hygienic_eq(impl_item.name, trait_item_name, trait_def_id)
356+
}).map(move |item| NodeItem { node: node, item: item })
354357
})
355358
}
356359
}

src/librustc/ty/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -2345,6 +2345,13 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
23452345
}
23462346
}
23472347

2348+
// Hygienically compare a use-site name (`use_name`) for a field or an associated item with its
2349+
// supposed definition name (`def_name`). The method also needs `DefId` of the supposed
2350+
// definition's parent/scope to perform comparison.
2351+
pub fn hygienic_eq(self, use_name: Name, def_name: Name, def_parent_def_id: DefId) -> bool {
2352+
self.adjust(use_name, def_parent_def_id, DUMMY_NODE_ID).0 == def_name.to_ident()
2353+
}
2354+
23482355
pub fn adjust(self, name: Name, scope: DefId, block: NodeId) -> (Ident, DefId) {
23492356
self.adjust_ident(name.to_ident(), scope, block)
23502357
}
@@ -2356,6 +2363,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
23562363
};
23572364
let scope = match ident.ctxt.adjust(expansion) {
23582365
Some(macro_def) => self.hir.definitions().macro_def_scope(macro_def),
2366+
None if block == DUMMY_NODE_ID => DefId::local(CRATE_DEF_INDEX), // Dummy DefId
23592367
None => self.hir.get_module_parent(block),
23602368
};
23612369
(ident, scope)

src/librustc/ty/sty.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -596,9 +596,10 @@ impl<'a, 'tcx> ProjectionTy<'tcx> {
596596
pub fn from_ref_and_name(
597597
tcx: TyCtxt, trait_ref: ty::TraitRef<'tcx>, item_name: Name
598598
) -> ProjectionTy<'tcx> {
599-
let item_def_id = tcx.associated_items(trait_ref.def_id).find(
600-
|item| item.name == item_name && item.kind == ty::AssociatedKind::Type
601-
).unwrap().def_id;
599+
let item_def_id = tcx.associated_items(trait_ref.def_id).find(|item| {
600+
item.kind == ty::AssociatedKind::Type &&
601+
tcx.hygienic_eq(item_name, item.name, trait_ref.def_id)
602+
}).unwrap().def_id;
602603

603604
ProjectionTy {
604605
substs: trait_ref.substs,

src/librustc_typeck/astconv.rs

+11-15
Original file line numberDiff line numberDiff line change
@@ -356,9 +356,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
356356
poly_projections.extend(assoc_bindings.iter().filter_map(|binding| {
357357
// specify type to assert that error was already reported in Err case:
358358
let predicate: Result<_, ErrorReported> =
359-
self.ast_type_binding_to_poly_projection_predicate(trait_ref.ref_id,
360-
poly_trait_ref,
361-
binding);
359+
self.ast_type_binding_to_poly_projection_predicate(poly_trait_ref, binding);
362360
predicate.ok() // ok to ignore Err() because ErrorReported (see above)
363361
}));
364362

@@ -423,13 +421,13 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
423421
-> bool
424422
{
425423
self.tcx().associated_items(trait_def_id).any(|item| {
426-
item.kind == ty::AssociatedKind::Type && item.name == assoc_name
424+
item.kind == ty::AssociatedKind::Type &&
425+
self.tcx().hygienic_eq(assoc_name, item.name, trait_def_id)
427426
})
428427
}
429428

430429
fn ast_type_binding_to_poly_projection_predicate(
431430
&self,
432-
_path_id: ast::NodeId,
433431
trait_ref: ty::PolyTraitRef<'tcx>,
434432
binding: &ConvertedBinding<'tcx>)
435433
-> Result<ty::PolyProjectionPredicate<'tcx>, ErrorReported>
@@ -504,7 +502,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
504502

505503
let candidate = self.one_bound_for_assoc_type(candidates,
506504
&trait_ref.to_string(),
507-
&binding.item_name.as_str(),
505+
binding.item_name,
508506
binding.span)?;
509507

510508
Ok(candidate.map_bound(|trait_ref| {
@@ -702,7 +700,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
702700
let param_name = tcx.hir.ty_param_name(param_node_id);
703701
self.one_bound_for_assoc_type(suitable_bounds,
704702
&param_name.as_str(),
705-
&assoc_name.as_str(),
703+
assoc_name,
706704
span)
707705
}
708706

@@ -712,7 +710,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
712710
fn one_bound_for_assoc_type<I>(&self,
713711
mut bounds: I,
714712
ty_param_name: &str,
715-
assoc_name: &str,
713+
assoc_name: ast::Name,
716714
span: Span)
717715
-> Result<ty::PolyTraitRef<'tcx>, ErrorReported>
718716
where I: Iterator<Item=ty::PolyTraitRef<'tcx>>
@@ -741,7 +739,8 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
741739

742740
for bound in bounds {
743741
let bound_span = self.tcx().associated_items(bound.def_id()).find(|item| {
744-
item.kind == ty::AssociatedKind::Type && item.name == assoc_name
742+
item.kind == ty::AssociatedKind::Type &&
743+
self.tcx().hygienic_eq(assoc_name, item.name, bound.def_id())
745744
})
746745
.and_then(|item| self.tcx().hir.span_if_local(item.def_id));
747746

@@ -802,10 +801,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
802801
.filter(|r| self.trait_defines_associated_type_named(r.def_id(),
803802
assoc_name));
804803

805-
match self.one_bound_for_assoc_type(candidates,
806-
"Self",
807-
&assoc_name.as_str(),
808-
span) {
804+
match self.one_bound_for_assoc_type(candidates, "Self", assoc_name, span) {
809805
Ok(bound) => bound,
810806
Err(ErrorReported) => return (tcx.types.err, Def::Err),
811807
}
@@ -830,14 +826,14 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
830826
};
831827

832828
let trait_did = bound.0.def_id;
833-
let item = tcx.associated_items(trait_did).find(|i| i.name == assoc_name)
829+
let (assoc_ident, def_scope) = tcx.adjust(assoc_name, trait_did, ref_id);
830+
let item = tcx.associated_items(trait_did).find(|i| i.name.to_ident() == assoc_ident)
834831
.expect("missing associated type");
835832

836833
let ty = self.projected_ty_from_poly_trait_ref(span, item.def_id, bound);
837834
let ty = self.normalize_ty(span, ty);
838835

839836
let def = Def::AssociatedTy(item.def_id);
840-
let def_scope = tcx.adjust(assoc_name, item.container.id(), ref_id).1;
841837
if !item.vis.is_accessible_from(def_scope, tcx) {
842838
let msg = format!("{} `{}` is private", def.kind_name(), assoc_name);
843839
tcx.sess.span_err(span, &msg);

src/librustc_typeck/check/method/mod.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
373373
/// and return it, or `None`, if no such item was defined there.
374374
pub fn associated_item(&self, def_id: DefId, item_name: ast::Name)
375375
-> Option<ty::AssociatedItem> {
376-
let ident = self.tcx.adjust(item_name, def_id, self.body_id).0;
377-
self.tcx.associated_items(def_id).find(|item| item.name.to_ident() == ident)
376+
self.tcx.associated_items(def_id)
377+
.find(|item| self.tcx.hygienic_eq(item_name, item.name, def_id))
378+
378379
}
379380
}

src/librustc_typeck/check/mod.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -1248,6 +1248,7 @@ fn report_forbidden_specialization<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
12481248

12491249
fn check_specialization_validity<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
12501250
trait_def: &ty::TraitDef,
1251+
trait_item: &ty::AssociatedItem,
12511252
impl_id: DefId,
12521253
impl_item: &hir::ImplItem)
12531254
{
@@ -1258,7 +1259,8 @@ fn check_specialization_validity<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
12581259
hir::ImplItemKind::Method(..) => ty::AssociatedKind::Method,
12591260
hir::ImplItemKind::Type(_) => ty::AssociatedKind::Type
12601261
};
1261-
let parent = ancestors.defs(tcx, impl_item.name, kind).skip(1).next()
1262+
1263+
let parent = ancestors.defs(tcx, trait_item.name, kind, trait_def.def_id).skip(1).next()
12621264
.map(|node_item| node_item.map(|parent| parent.defaultness));
12631265

12641266
if let Some(parent) = parent {
@@ -1290,7 +1292,7 @@ fn check_impl_items_against_trait<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
12901292
for impl_item in impl_items() {
12911293
let ty_impl_item = tcx.associated_item(tcx.hir.local_def_id(impl_item.id));
12921294
let ty_trait_item = tcx.associated_items(impl_trait_ref.def_id)
1293-
.find(|ac| ac.name == ty_impl_item.name);
1295+
.find(|ac| tcx.hygienic_eq(ty_impl_item.name, ac.name, impl_trait_ref.def_id));
12941296

12951297
// Check that impl definition matches trait definition
12961298
if let Some(ty_trait_item) = ty_trait_item {
@@ -1371,9 +1373,9 @@ fn check_impl_items_against_trait<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
13711373
}
13721374
}
13731375
}
1374-
}
13751376

1376-
check_specialization_validity(tcx, trait_def, impl_id, impl_item);
1377+
check_specialization_validity(tcx, trait_def, &ty_trait_item, impl_id, impl_item);
1378+
}
13771379
}
13781380

13791381
// Check for missing items from trait
@@ -1382,7 +1384,7 @@ fn check_impl_items_against_trait<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
13821384
let associated_type_overridden = overridden_associated_type.is_some();
13831385
for trait_item in tcx.associated_items(impl_trait_ref.def_id) {
13841386
let is_implemented = trait_def.ancestors(tcx, impl_id)
1385-
.defs(tcx, trait_item.name, trait_item.kind)
1387+
.defs(tcx, trait_item.name, trait_item.kind, impl_trait_ref.def_id)
13861388
.next()
13871389
.map(|node_item| !node_item.node.is_from_trait())
13881390
.unwrap_or(false);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// ignore-pretty pretty-printing is unhygienic
12+
13+
#![feature(decl_macro)]
14+
#![allow(unused)]
15+
16+
mod ok {
17+
macro mac_trait_item($method: ident) {
18+
fn $method();
19+
}
20+
21+
trait Tr {
22+
mac_trait_item!(method);
23+
}
24+
25+
macro mac_trait_impl() {
26+
impl Tr for u8 { // OK
27+
fn method() {} // OK
28+
}
29+
}
30+
31+
mac_trait_impl!();
32+
}
33+
34+
mod error {
35+
macro mac_trait_item() {
36+
fn method();
37+
}
38+
39+
trait Tr {
40+
mac_trait_item!();
41+
}
42+
43+
macro mac_trait_impl() {
44+
impl Tr for u8 { //~ ERROR not all trait items implemented, missing: `method`
45+
fn method() {} //~ ERROR method `method` is not a member of trait `Tr`
46+
}
47+
}
48+
49+
mac_trait_impl!();
50+
}
51+
52+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// ignore-pretty pretty-printing is unhygienic
12+
13+
#![feature(decl_macro, associated_type_defaults)]
14+
#![feature(rustc_attrs)]
15+
16+
trait Base {
17+
type AssocTy;
18+
fn f();
19+
}
20+
trait Derived: Base {
21+
fn g();
22+
}
23+
24+
macro mac() {
25+
type A = Base<AssocTy = u8>;
26+
type B = Derived<AssocTy = u8>;
27+
28+
impl Base for u8 {
29+
type AssocTy = u8;
30+
fn f() {
31+
let _: Self::AssocTy;
32+
}
33+
}
34+
impl Derived for u8 {
35+
fn g() {
36+
let _: Self::AssocTy;
37+
}
38+
}
39+
40+
fn h<T: Base, U: Derived>() {
41+
let _: T::AssocTy;
42+
let _: U::AssocTy;
43+
}
44+
}
45+
46+
mac!();
47+
48+
#[rustc_error]
49+
fn main() {} //~ ERROR compilation successful

0 commit comments

Comments
 (0)