Skip to content

Commit 976692f

Browse files
authored
Rollup merge of rust-lang#119766 - oli-obk:split_tait_and_atpit, r=compiler-errors
Split tait and impl trait in assoc items logic And simplify the assoc item logic where applicable. This separation shows that it is easier to reason about impl trait in assoc items compared with TAITs. See https://rust-lang.zulipchat.com/#narrow/stream/315482-t-compiler.2Fetc.2Fopaque-types/topic/impl.20trait.20in.20associated.20type for some discussion. The current plan is to try to stabilize impl trait in associated items before TAIT, as they do not have any issues with their defining scopes (see rust-lang#107645 for why this is not a trivial or uncontroversial topic).
2 parents 8201df7 + 5e5d135 commit 976692f

21 files changed

+615
-73
lines changed

compiler/rustc_hir_analysis/src/collect/type_of.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -530,9 +530,13 @@ pub(super) fn type_of_opaque(
530530
Ok(ty::EarlyBinder::bind(match tcx.hir_node_by_def_id(def_id) {
531531
Node::Item(item) => match item.kind {
532532
ItemKind::OpaqueTy(OpaqueTy {
533-
origin: hir::OpaqueTyOrigin::TyAlias { .. },
533+
origin: hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false },
534534
..
535535
}) => opaque::find_opaque_ty_constraints_for_tait(tcx, def_id),
536+
ItemKind::OpaqueTy(OpaqueTy {
537+
origin: hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: true },
538+
..
539+
}) => opaque::find_opaque_ty_constraints_for_impl_trait_in_assoc_type(tcx, def_id),
536540
// Opaque types desugared from `impl Trait`.
537541
ItemKind::OpaqueTy(&OpaqueTy {
538542
origin:

compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs

+72-6
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,60 @@ pub fn test_opaque_hidden_types(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed>
2323
res
2424
}
2525

26+
/// Checks "defining uses" of opaque `impl Trait` in associated types.
27+
/// These can only be defined by associated items of the same trait.
28+
#[instrument(skip(tcx), level = "debug")]
29+
pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
30+
tcx: TyCtxt<'_>,
31+
def_id: LocalDefId,
32+
) -> Ty<'_> {
33+
let mut parent_def_id = def_id;
34+
while tcx.def_kind(parent_def_id) == def::DefKind::OpaqueTy {
35+
// Account for `type Alias = impl Trait<Foo = impl Trait>;` (#116031)
36+
parent_def_id = tcx.local_parent(parent_def_id);
37+
}
38+
let impl_def_id = tcx.local_parent(parent_def_id);
39+
match tcx.def_kind(impl_def_id) {
40+
DefKind::Impl { .. } => {}
41+
other => bug!("invalid impl trait in assoc type parent: {other:?}"),
42+
}
43+
44+
let mut locator = TaitConstraintLocator { def_id, tcx, found: None, typeck_types: vec![] };
45+
46+
for &assoc_id in tcx.associated_item_def_ids(impl_def_id) {
47+
let assoc = tcx.associated_item(assoc_id);
48+
match assoc.kind {
49+
ty::AssocKind::Const | ty::AssocKind::Fn => {
50+
locator.check(assoc_id.expect_local(), ImplTraitSource::AssocTy)
51+
}
52+
// Associated types don't have bodies, so they can't constrain hidden types
53+
ty::AssocKind::Type => {}
54+
}
55+
}
56+
57+
if let Some(hidden) = locator.found {
58+
// Only check against typeck if we didn't already error
59+
if !hidden.ty.references_error() {
60+
for concrete_type in locator.typeck_types {
61+
if concrete_type.ty != tcx.erase_regions(hidden.ty)
62+
&& !(concrete_type, hidden).references_error()
63+
{
64+
hidden.report_mismatch(&concrete_type, def_id, tcx).emit();
65+
}
66+
}
67+
}
68+
69+
hidden.ty
70+
} else {
71+
let reported = tcx.dcx().emit_err(UnconstrainedOpaqueType {
72+
span: tcx.def_span(def_id),
73+
name: tcx.item_name(parent_def_id.to_def_id()),
74+
what: "impl",
75+
});
76+
Ty::new_error(tcx, reported)
77+
}
78+
}
79+
2680
/// Checks "defining uses" of opaque `impl Trait` types to ensure that they meet the restrictions
2781
/// laid for "higher-order pattern unification".
2882
/// This ensures that inference is tractable.
@@ -128,9 +182,15 @@ struct TaitConstraintLocator<'tcx> {
128182
typeck_types: Vec<ty::OpaqueHiddenType<'tcx>>,
129183
}
130184

185+
#[derive(Debug)]
186+
enum ImplTraitSource {
187+
AssocTy,
188+
TyAlias,
189+
}
190+
131191
impl TaitConstraintLocator<'_> {
132192
#[instrument(skip(self), level = "debug")]
133-
fn check(&mut self, item_def_id: LocalDefId) {
193+
fn check(&mut self, item_def_id: LocalDefId, source: ImplTraitSource) {
134194
// Don't try to check items that cannot possibly constrain the type.
135195
if !self.tcx.has_typeck_results(item_def_id) {
136196
debug!("no constraint: no typeck results");
@@ -182,7 +242,13 @@ impl TaitConstraintLocator<'_> {
182242
continue;
183243
}
184244
constrained = true;
185-
if !self.tcx.opaque_types_defined_by(item_def_id).contains(&self.def_id) {
245+
let opaque_types_defined_by = match source {
246+
ImplTraitSource::AssocTy => {
247+
self.tcx.impl_trait_in_assoc_types_defined_by(item_def_id)
248+
}
249+
ImplTraitSource::TyAlias => self.tcx.opaque_types_defined_by(item_def_id),
250+
};
251+
if !opaque_types_defined_by.contains(&self.def_id) {
186252
self.tcx.dcx().emit_err(TaitForwardCompat {
187253
span: hidden_type.span,
188254
item_span: self
@@ -240,29 +306,29 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> {
240306
}
241307
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
242308
if let hir::ExprKind::Closure(closure) = ex.kind {
243-
self.check(closure.def_id);
309+
self.check(closure.def_id, ImplTraitSource::TyAlias);
244310
}
245311
intravisit::walk_expr(self, ex);
246312
}
247313
fn visit_item(&mut self, it: &'tcx Item<'tcx>) {
248314
trace!(?it.owner_id);
249315
// The opaque type itself or its children are not within its reveal scope.
250316
if it.owner_id.def_id != self.def_id {
251-
self.check(it.owner_id.def_id);
317+
self.check(it.owner_id.def_id, ImplTraitSource::TyAlias);
252318
intravisit::walk_item(self, it);
253319
}
254320
}
255321
fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) {
256322
trace!(?it.owner_id);
257323
// The opaque type itself or its children are not within its reveal scope.
258324
if it.owner_id.def_id != self.def_id {
259-
self.check(it.owner_id.def_id);
325+
self.check(it.owner_id.def_id, ImplTraitSource::TyAlias);
260326
intravisit::walk_impl_item(self, it);
261327
}
262328
}
263329
fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) {
264330
trace!(?it.owner_id);
265-
self.check(it.owner_id.def_id);
331+
self.check(it.owner_id.def_id, ImplTraitSource::TyAlias);
266332
intravisit::walk_trait_item(self, it);
267333
}
268334
fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {

compiler/rustc_middle/src/query/mod.rs

+9
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,15 @@ rustc_queries! {
343343
}
344344
}
345345

346+
query impl_trait_in_assoc_types_defined_by(
347+
key: LocalDefId
348+
) -> &'tcx ty::List<LocalDefId> {
349+
desc {
350+
|tcx| "computing the opaque types defined by `{}`",
351+
tcx.def_path_str(key.to_def_id())
352+
}
353+
}
354+
346355
/// Returns the list of bounds that can be used for
347356
/// `SelectionCandidate::ProjectionCandidate(_)` and
348357
/// `ProjectionTyCandidate::TraitDef`.

compiler/rustc_ty_utils/src/opaque_types.rs

+151-62
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,69 @@ impl<'tcx> OpaqueTypeCollector<'tcx> {
118118
}
119119
TaitInBodyFinder { collector: self }.visit_expr(body);
120120
}
121+
122+
fn visit_opaque_ty(&mut self, alias_ty: &ty::AliasTy<'tcx>) {
123+
if !self.seen.insert(alias_ty.def_id.expect_local()) {
124+
return;
125+
}
126+
127+
// TAITs outside their defining scopes are ignored.
128+
let origin = self.tcx.opaque_type_origin(alias_ty.def_id.expect_local());
129+
trace!(?origin);
130+
match origin {
131+
rustc_hir::OpaqueTyOrigin::FnReturn(_) | rustc_hir::OpaqueTyOrigin::AsyncFn(_) => {}
132+
rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty } => {
133+
if !in_assoc_ty {
134+
if !self.check_tait_defining_scope(alias_ty.def_id.expect_local()) {
135+
return;
136+
}
137+
}
138+
}
139+
}
140+
141+
self.opaques.push(alias_ty.def_id.expect_local());
142+
143+
let parent_count = self.tcx.generics_of(alias_ty.def_id).parent_count;
144+
// Only check that the parent generics of the TAIT/RPIT are unique.
145+
// the args owned by the opaque are going to always be duplicate
146+
// lifetime params for RPITs, and empty for TAITs.
147+
match self
148+
.tcx
149+
.uses_unique_generic_params(&alias_ty.args[..parent_count], CheckRegions::FromFunction)
150+
{
151+
Ok(()) => {
152+
// FIXME: implement higher kinded lifetime bounds on nested opaque types. They are not
153+
// supported at all, so this is sound to do, but once we want to support them, you'll
154+
// start seeing the error below.
155+
156+
// Collect opaque types nested within the associated type bounds of this opaque type.
157+
// We use identity args here, because we already know that the opaque type uses
158+
// only generic parameters, and thus substituting would not give us more information.
159+
for (pred, span) in self
160+
.tcx
161+
.explicit_item_bounds(alias_ty.def_id)
162+
.instantiate_identity_iter_copied()
163+
{
164+
trace!(?pred);
165+
self.visit_spanned(span, pred);
166+
}
167+
}
168+
Err(NotUniqueParam::NotParam(arg)) => {
169+
self.tcx.dcx().emit_err(NotParam {
170+
arg,
171+
span: self.span(),
172+
opaque_span: self.tcx.def_span(alias_ty.def_id),
173+
});
174+
}
175+
Err(NotUniqueParam::DuplicateParam(arg)) => {
176+
self.tcx.dcx().emit_err(DuplicateArg {
177+
arg,
178+
span: self.span(),
179+
opaque_span: self.tcx.def_span(alias_ty.def_id),
180+
});
181+
}
182+
}
183+
}
121184
}
122185

123186
impl<'tcx> super::sig_types::SpannedTypeVisitor<'tcx> for OpaqueTypeCollector<'tcx> {
@@ -134,67 +197,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
134197
t.super_visit_with(self)?;
135198
match t.kind() {
136199
ty::Alias(ty::Opaque, alias_ty) if alias_ty.def_id.is_local() => {
137-
if !self.seen.insert(alias_ty.def_id.expect_local()) {
138-
return ControlFlow::Continue(());
139-
}
140-
141-
// TAITs outside their defining scopes are ignored.
142-
let origin = self.tcx.opaque_type_origin(alias_ty.def_id.expect_local());
143-
trace!(?origin);
144-
match origin {
145-
rustc_hir::OpaqueTyOrigin::FnReturn(_)
146-
| rustc_hir::OpaqueTyOrigin::AsyncFn(_) => {}
147-
rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty } => {
148-
if !in_assoc_ty {
149-
if !self.check_tait_defining_scope(alias_ty.def_id.expect_local()) {
150-
return ControlFlow::Continue(());
151-
}
152-
}
153-
}
154-
}
155-
156-
self.opaques.push(alias_ty.def_id.expect_local());
157-
158-
let parent_count = self.tcx.generics_of(alias_ty.def_id).parent_count;
159-
// Only check that the parent generics of the TAIT/RPIT are unique.
160-
// the args owned by the opaque are going to always be duplicate
161-
// lifetime params for RPITs, and empty for TAITs.
162-
match self.tcx.uses_unique_generic_params(
163-
&alias_ty.args[..parent_count],
164-
CheckRegions::FromFunction,
165-
) {
166-
Ok(()) => {
167-
// FIXME: implement higher kinded lifetime bounds on nested opaque types. They are not
168-
// supported at all, so this is sound to do, but once we want to support them, you'll
169-
// start seeing the error below.
170-
171-
// Collect opaque types nested within the associated type bounds of this opaque type.
172-
// We use identity args here, because we already know that the opaque type uses
173-
// only generic parameters, and thus substituting would not give us more information.
174-
for (pred, span) in self
175-
.tcx
176-
.explicit_item_bounds(alias_ty.def_id)
177-
.instantiate_identity_iter_copied()
178-
{
179-
trace!(?pred);
180-
self.visit_spanned(span, pred);
181-
}
182-
}
183-
Err(NotUniqueParam::NotParam(arg)) => {
184-
self.tcx.dcx().emit_err(NotParam {
185-
arg,
186-
span: self.span(),
187-
opaque_span: self.tcx.def_span(alias_ty.def_id),
188-
});
189-
}
190-
Err(NotUniqueParam::DuplicateParam(arg)) => {
191-
self.tcx.dcx().emit_err(DuplicateArg {
192-
arg,
193-
span: self.span(),
194-
opaque_span: self.tcx.def_span(alias_ty.def_id),
195-
});
196-
}
197-
}
200+
self.visit_opaque_ty(alias_ty);
198201
}
199202
ty::Alias(ty::Weak, alias_ty) if alias_ty.def_id.is_local() => {
200203
self.tcx
@@ -272,6 +275,91 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
272275
}
273276
}
274277

278+
struct ImplTraitInAssocTypeCollector<'tcx>(OpaqueTypeCollector<'tcx>);
279+
280+
impl<'tcx> super::sig_types::SpannedTypeVisitor<'tcx> for ImplTraitInAssocTypeCollector<'tcx> {
281+
#[instrument(skip(self), ret, level = "trace")]
282+
fn visit(&mut self, span: Span, value: impl TypeVisitable<TyCtxt<'tcx>>) -> ControlFlow<!> {
283+
let old = self.0.span;
284+
self.0.span = Some(span);
285+
value.visit_with(self);
286+
self.0.span = old;
287+
288+
ControlFlow::Continue(())
289+
}
290+
}
291+
292+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInAssocTypeCollector<'tcx> {
293+
#[instrument(skip(self), ret, level = "trace")]
294+
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<!> {
295+
t.super_visit_with(self)?;
296+
match t.kind() {
297+
ty::Alias(ty::Opaque, alias_ty) if alias_ty.def_id.is_local() => {
298+
self.0.visit_opaque_ty(alias_ty);
299+
}
300+
ty::Alias(ty::Projection, alias_ty) => {
301+
// This avoids having to do normalization of `Self::AssocTy` by only
302+
// supporting the case of a method defining opaque types from assoc types
303+
// in the same impl block.
304+
let parent_trait_ref = self
305+
.0
306+
.parent_trait_ref()
307+
.expect("impl trait in assoc type collector used on non-assoc item");
308+
// If the trait ref of the associated item and the impl differs,
309+
// then we can't use the impl's identity substitutions below, so
310+
// just skip.
311+
if alias_ty.trait_ref(self.0.tcx) == parent_trait_ref {
312+
let parent = self.0.parent().expect("we should have a parent here");
313+
314+
for &assoc in self.0.tcx.associated_items(parent).in_definition_order() {
315+
trace!(?assoc);
316+
if assoc.trait_item_def_id != Some(alias_ty.def_id) {
317+
continue;
318+
}
319+
320+
// If the type is further specializable, then the type_of
321+
// is not actually correct below.
322+
if !assoc.defaultness(self.0.tcx).is_final() {
323+
continue;
324+
}
325+
326+
let impl_args = alias_ty.args.rebase_onto(
327+
self.0.tcx,
328+
parent_trait_ref.def_id,
329+
ty::GenericArgs::identity_for_item(self.0.tcx, parent),
330+
);
331+
332+
if check_args_compatible(self.0.tcx, assoc, impl_args) {
333+
return self
334+
.0
335+
.tcx
336+
.type_of(assoc.def_id)
337+
.instantiate(self.0.tcx, impl_args)
338+
.visit_with(self);
339+
} else {
340+
self.0.tcx.dcx().span_delayed_bug(
341+
self.0.tcx.def_span(assoc.def_id),
342+
"item had incorrect args",
343+
);
344+
}
345+
}
346+
}
347+
}
348+
_ => trace!(kind=?t.kind()),
349+
}
350+
ControlFlow::Continue(())
351+
}
352+
}
353+
354+
fn impl_trait_in_assoc_types_defined_by<'tcx>(
355+
tcx: TyCtxt<'tcx>,
356+
item: LocalDefId,
357+
) -> &'tcx ty::List<LocalDefId> {
358+
let mut collector = ImplTraitInAssocTypeCollector(OpaqueTypeCollector::new(tcx, item));
359+
super::sig_types::walk_types(tcx, item, &mut collector);
360+
tcx.mk_local_def_ids(&collector.0.opaques)
361+
}
362+
275363
fn opaque_types_defined_by<'tcx>(
276364
tcx: TyCtxt<'tcx>,
277365
item: LocalDefId,
@@ -321,5 +409,6 @@ fn opaque_types_defined_by<'tcx>(
321409
}
322410

323411
pub(super) fn provide(providers: &mut Providers) {
324-
*providers = Providers { opaque_types_defined_by, ..*providers };
412+
*providers =
413+
Providers { opaque_types_defined_by, impl_trait_in_assoc_types_defined_by, ..*providers };
325414
}

0 commit comments

Comments
 (0)