Skip to content

Commit 0d3e377

Browse files
committed
Reject escaping bound vars in the type of assoc const bindings
1 parent f65197a commit 0d3e377

8 files changed

+202
-27
lines changed

compiler/rustc_hir_analysis/messages.ftl

+5
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,11 @@ hir_analysis_enum_discriminant_overflowed = enum discriminant overflowed
122122
.label = overflowed on value after {$discr}
123123
.note = explicitly set `{$item_name} = {$wrapped_discr}` if that is desired outcome
124124
125+
hir_analysis_escaping_bound_var_in_ty_of_assoc_const_binding =
126+
the type of the associated constant `{$assoc_const}` cannot capture late-bound generic parameters
127+
.label = its type cannot capture the late-bound {$var_def_kind} `{$var_name}`
128+
.var_defined_here_label = the late-bound {$var_def_kind} `{$var_name}` is defined here
129+
125130
hir_analysis_field_already_declared =
126131
field `{$field_name}` is already declared
127132
.label = field already declared

compiler/rustc_hir_analysis/src/collect/type_of.rs

+88-27
Original file line numberDiff line numberDiff line change
@@ -71,22 +71,17 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
7171
}
7272

7373
Node::TypeBinding(&TypeBinding { hir_id, ident, .. }) => {
74-
let ty = tcx.type_of_assoc_const_binding(hir_id);
74+
let ty = tcx.type_of_assoc_const_binding(hir_id).skip_binder().skip_binder();
7575

7676
// We can't possibly catch this in the resolver, therefore we need to handle it here.
7777
// FIXME(const_generics): Support generic const generics.
78-
let Some(ty) = ty.no_bound_vars() else {
79-
let reported = report_overly_generic_assoc_const_binding_type(
80-
tcx,
81-
ident,
82-
ty.skip_binder().skip_binder(),
83-
hir_id,
84-
);
78+
if ty.has_param() || ty.has_escaping_bound_vars() {
79+
let reported =
80+
report_overly_generic_assoc_const_binding_type(tcx, ident, ty, hir_id);
8581
return Ty::new_error(tcx, reported);
8682
};
8783

88-
// FIXME(fmease): Reject escaping late-bound vars.
89-
return ty.skip_binder();
84+
return ty;
9085
}
9186

9287
// This match arm is for when the def_id appears in a GAT whose
@@ -313,8 +308,15 @@ fn report_overly_generic_assoc_const_binding_type<'tcx>(
313308
ty: Ty<'tcx>,
314309
hir_id: HirId,
315310
) -> ErrorGuaranteed {
316-
let mut collector = GenericParamCollector { params: Default::default() };
317-
ty.visit_with(&mut collector);
311+
let mut collector = GenericParamAndBoundVarCollector {
312+
tcx,
313+
params: Default::default(),
314+
vars: Default::default(),
315+
depth: ty::INNERMOST,
316+
};
317+
if let ControlFlow::Break(reported) = ty.visit_with(&mut collector) {
318+
return reported;
319+
}
318320

319321
let mut reported = None;
320322

@@ -331,40 +333,99 @@ fn report_overly_generic_assoc_const_binding_type<'tcx>(
331333
param_defined_here_label: tcx.def_ident_span(param_def.def_id).unwrap(),
332334
}));
333335
}
336+
for (var_def_id, var_name) in collector.vars {
337+
reported.get_or_insert(tcx.dcx().emit_err(
338+
crate::errors::EscapingBoundVarInTyOfAssocConstBinding {
339+
span: assoc_const.span,
340+
assoc_const,
341+
var_name,
342+
var_def_kind: tcx.def_descr(var_def_id),
343+
var_defined_here_label: tcx.def_ident_span(var_def_id).unwrap(),
344+
},
345+
));
346+
}
334347

335-
struct GenericParamCollector {
348+
struct GenericParamAndBoundVarCollector<'tcx> {
349+
tcx: TyCtxt<'tcx>,
336350
params: FxIndexSet<(u32, Symbol)>,
351+
vars: FxIndexSet<(DefId, Symbol)>,
352+
depth: ty::DebruijnIndex,
337353
}
338354

339-
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamCollector {
340-
type BreakTy = !;
355+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamAndBoundVarCollector<'tcx> {
356+
type BreakTy = ErrorGuaranteed;
357+
358+
fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
359+
&mut self,
360+
binder: &ty::Binder<'tcx, T>,
361+
) -> ControlFlow<Self::BreakTy> {
362+
self.depth.shift_in(1);
363+
let binder = binder.super_visit_with(self);
364+
self.depth.shift_out(1);
365+
binder
366+
}
341367

342368
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
343-
if let ty::Param(param) = ty.kind() {
344-
self.params.insert((param.index, param.name));
345-
return ControlFlow::Continue(());
369+
match ty.kind() {
370+
ty::Param(param) => {
371+
self.params.insert((param.index, param.name));
372+
}
373+
ty::Bound(db, bt) if *db >= self.depth => {
374+
self.vars.insert(match bt.kind {
375+
ty::BoundTyKind::Param(def_id, name) => (def_id, name),
376+
ty::BoundTyKind::Anon => {
377+
let reported = self
378+
.tcx
379+
.dcx()
380+
.delayed_bug(format!("unexpected anon bound ty: {:?}", bt.var));
381+
return ControlFlow::Break(reported);
382+
}
383+
});
384+
}
385+
_ => return ty.super_visit_with(self),
346386
}
347-
ty.super_visit_with(self)
387+
ControlFlow::Continue(())
348388
}
349389

350390
fn visit_region(&mut self, re: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
351-
if let ty::ReEarlyParam(param) = re.kind() {
352-
self.params.insert((param.index, param.name));
353-
return ControlFlow::Continue(());
391+
match re.kind() {
392+
ty::ReEarlyParam(param) => {
393+
self.params.insert((param.index, param.name));
394+
}
395+
ty::ReBound(db, br) if db >= self.depth => {
396+
self.vars.insert(match br.kind {
397+
ty::BrNamed(def_id, name) => (def_id, name),
398+
ty::BrAnon | ty::BrEnv => {
399+
let reported = self.tcx.dcx().delayed_bug(format!(
400+
"unexpected bound region kind: {:?}",
401+
br.kind
402+
));
403+
return ControlFlow::Break(reported);
404+
}
405+
});
406+
}
407+
_ => {}
354408
}
355409
ControlFlow::Continue(())
356410
}
357411

358412
fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
359-
if let ty::ConstKind::Param(param) = ct.kind() {
360-
self.params.insert((param.index, param.name));
361-
return ControlFlow::Continue(());
413+
match ct.kind() {
414+
ty::ConstKind::Param(param) => {
415+
self.params.insert((param.index, param.name));
416+
ControlFlow::Continue(())
417+
}
418+
ty::ConstKind::Bound(db, ty::BoundVar { .. }) if db >= self.depth => {
419+
let reported =
420+
self.tcx.dcx().delayed_bug("unexpected escaping late-bound const var");
421+
ControlFlow::Break(reported)
422+
}
423+
_ => ct.super_visit_with(self),
362424
}
363-
ct.super_visit_with(self)
364425
}
365426
}
366427

367-
reported.unwrap_or_else(|| bug!("failed to find gen params in ty"))
428+
reported.unwrap_or_else(|| bug!("failed to find gen params or bound vars in ty"))
368429
}
369430

370431
fn get_path_containing_arg_in_pat<'hir>(

compiler/rustc_hir_analysis/src/errors.rs

+13
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,19 @@ pub(crate) struct ParamInTyOfAssocConstBinding {
268268
pub param_defined_here_label: Span,
269269
}
270270

271+
#[derive(Diagnostic)]
272+
#[diag(hir_analysis_escaping_bound_var_in_ty_of_assoc_const_binding)]
273+
pub(crate) struct EscapingBoundVarInTyOfAssocConstBinding {
274+
#[primary_span]
275+
#[label]
276+
pub span: Span,
277+
pub assoc_const: Ident,
278+
pub var_name: Symbol,
279+
pub var_def_kind: &'static str,
280+
#[label(hir_analysis_var_defined_here_label)]
281+
pub var_defined_here_label: Span,
282+
}
283+
271284
#[derive(Subdiagnostic)]
272285
#[help(hir_analysis_parenthesized_fn_trait_expansion)]
273286
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,14 @@
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+
14+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
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+
error: aborting due to 1 previous error
10+

0 commit comments

Comments
 (0)