Skip to content

Commit 76e4484

Browse files
committed
Reject escaping bound vars in the type of assoc const bindings
1 parent 4b7db5a commit 76e4484

8 files changed

+206
-29
lines changed

compiler/rustc_hir_analysis/messages.ftl

+5
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@ hir_analysis_enum_discriminant_overflowed = enum discriminant overflowed
118118
.label = overflowed on value after {$discr}
119119
.note = explicitly set `{$item_name} = {$wrapped_discr}` if that is desired outcome
120120
121+
hir_analysis_escaping_bound_var_in_ty_of_assoc_const_binding =
122+
the type of the associated constant `{$assoc_const}` cannot capture late-bound generic parameters
123+
.label = its type cannot capture the late-bound {$var_def_kind} `{$var_name}`
124+
.var_defined_here_label = the late-bound {$var_def_kind} `{$var_name}` is defined here
125+
121126
hir_analysis_field_already_declared =
122127
field `{$field_name}` is already declared
123128
.label = field already declared

compiler/rustc_hir_analysis/src/collect/type_of.rs

+87-29
Original file line numberDiff line numberDiff line change
@@ -83,22 +83,17 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
8383
}
8484

8585
Node::TypeBinding(&TypeBinding { hir_id, ident, .. }) => {
86-
let ty = tcx.type_of_assoc_const_binding(hir_id);
86+
let ty = tcx.type_of_assoc_const_binding(hir_id).skip_binder().skip_binder();
8787

8888
// We can't possibly catch this in the resolver, therefore we need to handle it here.
8989
// FIXME(const_generics): Support generic const generics.
90-
let Some(ty) = ty.no_bound_vars() else {
91-
let reported = report_overly_generic_assoc_const_binding_type(
92-
tcx,
93-
ident,
94-
ty.skip_binder().skip_binder(),
95-
hir_id,
96-
);
90+
if ty.has_param() || ty.has_escaping_bound_vars() {
91+
let reported =
92+
report_overly_generic_assoc_const_binding_type(tcx, ident, ty, hir_id);
9793
return Ty::new_error(tcx, reported);
9894
};
9995

100-
// FIXME(fmease): Reject escaping late-bound vars.
101-
return ty.skip_binder();
96+
return ty;
10297
}
10398

10499
// This match arm is for when the def_id appears in a GAT whose
@@ -325,10 +320,13 @@ fn report_overly_generic_assoc_const_binding_type<'tcx>(
325320
ty: Ty<'tcx>,
326321
hir_id: HirId,
327322
) -> ErrorGuaranteed {
328-
let mut collector = GenericParamCollector { params: Default::default() };
329-
ty.visit_with(&mut collector);
330-
331-
let mut reported = None;
323+
let mut collector = GenericParamAndBoundVarCollector {
324+
tcx,
325+
params: Default::default(),
326+
vars: Default::default(),
327+
depth: ty::INNERMOST,
328+
};
329+
let mut reported = ty.visit_with(&mut collector).break_value();
332330

333331
let ty_note = ty
334332
.make_suggestable(tcx, false)
@@ -363,40 +361,100 @@ fn report_overly_generic_assoc_const_binding_type<'tcx>(
363361
ty_note,
364362
}));
365363
}
364+
for (var_def_id, var_name) in collector.vars {
365+
reported.get_or_insert(tcx.dcx().emit_err(
366+
crate::errors::EscapingBoundVarInTyOfAssocConstBinding {
367+
span: assoc_const.span,
368+
assoc_const,
369+
var_name,
370+
var_def_kind: tcx.def_descr(var_def_id),
371+
var_defined_here_label: tcx.def_ident_span(var_def_id).unwrap(),
372+
ty_note,
373+
},
374+
));
375+
}
366376

367-
struct GenericParamCollector {
377+
struct GenericParamAndBoundVarCollector<'tcx> {
378+
tcx: TyCtxt<'tcx>,
368379
params: FxIndexSet<(u32, Symbol)>,
380+
vars: FxIndexSet<(DefId, Symbol)>,
381+
depth: ty::DebruijnIndex,
369382
}
370383

371-
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamCollector {
372-
type BreakTy = !;
384+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamAndBoundVarCollector<'tcx> {
385+
type BreakTy = ErrorGuaranteed;
386+
387+
fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
388+
&mut self,
389+
binder: &ty::Binder<'tcx, T>,
390+
) -> ControlFlow<Self::BreakTy> {
391+
self.depth.shift_in(1);
392+
let binder = binder.super_visit_with(self);
393+
self.depth.shift_out(1);
394+
binder
395+
}
373396

374397
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
375-
if let ty::Param(param) = ty.kind() {
376-
self.params.insert((param.index, param.name));
377-
return ControlFlow::Continue(());
398+
match ty.kind() {
399+
ty::Param(param) => {
400+
self.params.insert((param.index, param.name));
401+
}
402+
ty::Bound(db, bt) if *db >= self.depth => {
403+
self.vars.insert(match bt.kind {
404+
ty::BoundTyKind::Param(def_id, name) => (def_id, name),
405+
ty::BoundTyKind::Anon => {
406+
let reported = self
407+
.tcx
408+
.dcx()
409+
.delayed_bug(format!("unexpected anon bound ty: {:?}", bt.var));
410+
return ControlFlow::Break(reported);
411+
}
412+
});
413+
}
414+
_ => return ty.super_visit_with(self),
378415
}
379-
ty.super_visit_with(self)
416+
ControlFlow::Continue(())
380417
}
381418

382419
fn visit_region(&mut self, re: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
383-
if let ty::ReEarlyParam(param) = re.kind() {
384-
self.params.insert((param.index, param.name));
385-
return ControlFlow::Continue(());
420+
match re.kind() {
421+
ty::ReEarlyParam(param) => {
422+
self.params.insert((param.index, param.name));
423+
}
424+
ty::ReBound(db, br) if db >= self.depth => {
425+
self.vars.insert(match br.kind {
426+
ty::BrNamed(def_id, name) => (def_id, name),
427+
ty::BrAnon | ty::BrEnv => {
428+
let reported = self.tcx.dcx().delayed_bug(format!(
429+
"unexpected bound region kind: {:?}",
430+
br.kind
431+
));
432+
return ControlFlow::Break(reported);
433+
}
434+
});
435+
}
436+
_ => {}
386437
}
387438
ControlFlow::Continue(())
388439
}
389440

390441
fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
391-
if let ty::ConstKind::Param(param) = ct.kind() {
392-
self.params.insert((param.index, param.name));
393-
return ControlFlow::Continue(());
442+
match ct.kind() {
443+
ty::ConstKind::Param(param) => {
444+
self.params.insert((param.index, param.name));
445+
ControlFlow::Continue(())
446+
}
447+
ty::ConstKind::Bound(db, ty::BoundVar { .. }) if db >= self.depth => {
448+
let reported =
449+
self.tcx.dcx().delayed_bug("unexpected escaping late-bound const var");
450+
ControlFlow::Break(reported)
451+
}
452+
_ => ct.super_visit_with(self),
394453
}
395-
ct.super_visit_with(self)
396454
}
397455
}
398456

399-
reported.unwrap_or_else(|| bug!("failed to find gen params in ty"))
457+
reported.unwrap_or_else(|| bug!("failed to find gen params or bound vars in ty"))
400458
}
401459

402460
fn get_path_containing_arg_in_pat<'hir>(

compiler/rustc_hir_analysis/src/errors.rs

+15
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,21 @@ pub(crate) struct TyOfAssocConstBindingNote<'tcx> {
318318
pub ty: Ty<'tcx>,
319319
}
320320

321+
#[derive(Diagnostic)]
322+
#[diag(hir_analysis_escaping_bound_var_in_ty_of_assoc_const_binding)]
323+
pub(crate) struct EscapingBoundVarInTyOfAssocConstBinding<'tcx> {
324+
#[primary_span]
325+
#[label]
326+
pub span: Span,
327+
pub assoc_const: Ident,
328+
pub var_name: Symbol,
329+
pub var_def_kind: &'static str,
330+
#[label(hir_analysis_var_defined_here_label)]
331+
pub var_defined_here_label: Span,
332+
#[subdiagnostic]
333+
pub ty_note: Option<TyOfAssocConstBindingNote<'tcx>>,
334+
}
335+
321336
#[derive(Subdiagnostic)]
322337
#[help(hir_analysis_parenthesized_fn_trait_expansion)]
323338
pub struct ParenthesizedFnTraitExpansion {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Check that we eventually catch types of assoc const bounds
2+
// (containing late-bound vars) that are ill-formed.
3+
#![feature(associated_const_equality)]
4+
5+
trait Trait<T> {
6+
const K: T;
7+
}
8+
9+
fn take(
10+
_: impl Trait<
11+
<<for<'a> fn(&'a str) -> &'a str as Project>::Out as Discard>::Out,
12+
K = { () }
13+
>,
14+
) {}
15+
//~^^^^^^ ERROR implementation of `Project` is not general enough
16+
//~^^^^ ERROR higher-ranked subtype error
17+
//~| ERROR higher-ranked subtype error
18+
19+
trait Project { type Out; }
20+
impl<T> Project for fn(T) -> T { type Out = T; }
21+
22+
trait Discard { type Out; }
23+
impl<T: ?Sized> Discard for T { type Out = (); }
24+
25+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error: higher-ranked subtype error
2+
--> $DIR/assoc-const-eq-bound-var-in-ty-not-wf.rs:12:13
3+
|
4+
LL | K = { () }
5+
| ^^^^^^
6+
7+
error: higher-ranked subtype error
8+
--> $DIR/assoc-const-eq-bound-var-in-ty-not-wf.rs:12:13
9+
|
10+
LL | K = { () }
11+
| ^^^^^^
12+
|
13+
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
14+
15+
error: implementation of `Project` is not general enough
16+
--> $DIR/assoc-const-eq-bound-var-in-ty-not-wf.rs:9:4
17+
|
18+
LL | fn take(
19+
| ^^^^ implementation of `Project` is not general enough
20+
|
21+
= note: `Project` would have to be implemented for the type `for<'a> fn(&'a str) -> &'a str`
22+
= note: ...but `Project` is actually implemented for the type `fn(&'0 str) -> &'0 str`, for some specific lifetime `'0`
23+
24+
error: aborting due to 3 previous errors
25+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Check that we don't reject non-escaping late-bound vars in the type of assoc const bindings.
2+
// There's no reason why we should disallow them.
3+
//
4+
//@ check-pass
5+
6+
#![feature(associated_const_equality)]
7+
8+
trait Trait<T> {
9+
const K: T;
10+
}
11+
12+
fn take(
13+
_: impl Trait<
14+
<for<'a> fn(&'a str) -> &'a str as Discard>::Out,
15+
K = { () }
16+
>,
17+
) {}
18+
19+
trait Discard { type Out; }
20+
impl<T: ?Sized> Discard for T { type Out = (); }
21+
22+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Detect and reject escaping late-bound generic params in
2+
// the type of assoc consts used in an equality bound.
3+
#![feature(associated_const_equality)]
4+
5+
trait Trait<'a> {
6+
const K: &'a ();
7+
}
8+
9+
fn take(_: impl for<'r> Trait<'r, K = { &() }>) {}
10+
//~^ ERROR the type of the associated constant `K` cannot capture late-bound generic parameters
11+
//~| NOTE its type cannot capture the late-bound lifetime parameter `'r`
12+
//~| NOTE the late-bound lifetime parameter `'r` is defined here
13+
//~| NOTE `K` has type `&'r ()`
14+
15+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error: the type of the associated constant `K` cannot capture late-bound generic parameters
2+
--> $DIR/assoc-const-eq-esc-bound-var-in-ty.rs:9:35
3+
|
4+
LL | fn take(_: impl for<'r> Trait<'r, K = { &() }>) {}
5+
| -- ^ its type cannot capture the late-bound lifetime parameter `'r`
6+
| |
7+
| the late-bound lifetime parameter `'r` is defined here
8+
|
9+
= note: `K` has type `&'r ()`
10+
11+
error: aborting due to 1 previous error
12+

0 commit comments

Comments
 (0)