Skip to content

Commit 0b2fb8d

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

8 files changed

+212
-20
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/astconv/bounds.rs

+93-20
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1+
use std::ops::ControlFlow;
2+
13
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
24
use rustc_errors::{codes::*, struct_span_code_err};
35
use rustc_hir as hir;
46
use rustc_hir::def::{DefKind, Res};
57
use rustc_hir::def_id::{DefId, LocalDefId};
68
use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt};
79
use rustc_span::symbol::Ident;
8-
use rustc_span::{ErrorGuaranteed, Span};
10+
use rustc_span::{ErrorGuaranteed, Span, Symbol};
911
use rustc_trait_selection::traits;
1012
use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
1113
use smallvec::SmallVec;
@@ -533,7 +535,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
533535
}
534536
}
535537

536-
/// Detect and reject early-bound generic params in the type of associated const bindings.
538+
/// Detect and reject early-bound & escaping late-bound generic params in the type of assoc const bindings.
537539
///
538540
/// FIXME(const_generics): This is a temporary and semi-artifical restriction until the
539541
/// arrival of *generic const generics*[^1].
@@ -552,17 +554,23 @@ fn check_assoc_const_binding_type<'tcx>(
552554
) -> Ty<'tcx> {
553555
// We can't perform the checks for early-bound params during name resolution unlike E0770
554556
// because this information depends on *type* resolution.
557+
// We can't perform these checks in `resolve_bound_vars` either for the same reason.
558+
// Consider the trait ref `for<'a> Trait<'a, C = { &0 }>`. We need to know the fully
559+
// resolved type of `Trait::C` in order to know if it references `'a` or not.
555560

556-
// FIXME(fmease): Reject escaping late-bound vars.
557561
let ty = ty.skip_binder();
558-
if !ty.has_param() {
562+
if !ty.has_param() && !ty.has_escaping_bound_vars() {
559563
return ty;
560564
}
561565

562-
let mut collector = GenericParamCollector { params: Default::default() };
563-
ty.visit_with(&mut collector);
566+
let mut collector = GenericParamAndBoundVarCollector {
567+
tcx,
568+
params: Default::default(),
569+
vars: Default::default(),
570+
depth: ty::INNERMOST,
571+
};
572+
let mut guar = ty.visit_with(&mut collector).break_value();
564573

565-
let mut guar = None;
566574
let ty_note = ty
567575
.make_suggestable(tcx, false)
568576
.map(|ty| crate::errors::TyOfAssocConstBindingNote { assoc_const, ty });
@@ -593,35 +601,100 @@ fn check_assoc_const_binding_type<'tcx>(
593601
ty_note,
594602
}));
595603
}
604+
for (var_def_id, var_name) in collector.vars {
605+
guar.get_or_insert(tcx.dcx().emit_err(
606+
crate::errors::EscapingBoundVarInTyOfAssocConstBinding {
607+
span: assoc_const.span,
608+
assoc_const,
609+
var_name,
610+
var_def_kind: tcx.def_descr(var_def_id),
611+
var_defined_here_label: tcx.def_ident_span(var_def_id).unwrap(),
612+
ty_note,
613+
},
614+
));
615+
}
596616

597-
let guar = guar.unwrap_or_else(|| bug!("failed to find gen params in ty"));
617+
let guar = guar.unwrap_or_else(|| bug!("failed to find gen params or bound vars in ty"));
598618
Ty::new_error(tcx, guar)
599619
}
600620

601-
struct GenericParamCollector {
621+
struct GenericParamAndBoundVarCollector<'tcx> {
622+
tcx: TyCtxt<'tcx>,
602623
params: FxIndexSet<u32>,
624+
vars: FxIndexSet<(DefId, Symbol)>,
625+
depth: ty::DebruijnIndex,
603626
}
604627

605-
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamCollector {
628+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamAndBoundVarCollector<'tcx> {
629+
type Result = ControlFlow<ErrorGuaranteed>;
630+
631+
fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
632+
&mut self,
633+
binder: &ty::Binder<'tcx, T>,
634+
) -> Self::Result {
635+
self.depth.shift_in(1);
636+
let result = binder.super_visit_with(self);
637+
self.depth.shift_out(1);
638+
result
639+
}
640+
606641
fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
607-
if let ty::Param(param) = ty.kind() {
608-
self.params.insert(param.index);
609-
} else if ty.has_param() {
610-
ty.super_visit_with(self)
642+
match ty.kind() {
643+
ty::Param(param) => {
644+
self.params.insert(param.index);
645+
}
646+
ty::Bound(db, bt) if *db >= self.depth => {
647+
self.vars.insert(match bt.kind {
648+
ty::BoundTyKind::Param(def_id, name) => (def_id, name),
649+
ty::BoundTyKind::Anon => {
650+
let reported = self
651+
.tcx
652+
.dcx()
653+
.delayed_bug(format!("unexpected anon bound ty: {:?}", bt.var));
654+
return ControlFlow::Break(reported);
655+
}
656+
});
657+
}
658+
_ if ty.has_param() || ty.has_bound_vars() => return ty.super_visit_with(self),
659+
_ => {}
611660
}
661+
ControlFlow::Continue(())
612662
}
613663

614664
fn visit_region(&mut self, re: ty::Region<'tcx>) -> Self::Result {
615-
if let ty::ReEarlyParam(param) = re.kind() {
616-
self.params.insert(param.index);
665+
match re.kind() {
666+
ty::ReEarlyParam(param) => {
667+
self.params.insert(param.index);
668+
}
669+
ty::ReBound(db, br) if db >= self.depth => {
670+
self.vars.insert(match br.kind {
671+
ty::BrNamed(def_id, name) => (def_id, name),
672+
ty::BrAnon | ty::BrEnv => {
673+
let guar = self
674+
.tcx
675+
.dcx()
676+
.delayed_bug(format!("unexpected bound region kind: {:?}", br.kind));
677+
return ControlFlow::Break(guar);
678+
}
679+
});
680+
}
681+
_ => {}
617682
}
683+
ControlFlow::Continue(())
618684
}
619685

620686
fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result {
621-
if let ty::ConstKind::Param(param) = ct.kind() {
622-
self.params.insert(param.index);
623-
} else if ct.has_param() {
624-
ct.super_visit_with(self)
687+
match ct.kind() {
688+
ty::ConstKind::Param(param) => {
689+
self.params.insert(param.index);
690+
}
691+
ty::ConstKind::Bound(db, ty::BoundVar { .. }) if db >= self.depth => {
692+
let guar = self.tcx.dcx().delayed_bug("unexpected escaping late-bound const var");
693+
return ControlFlow::Break(guar);
694+
}
695+
_ if ct.has_param() || ct.has_bound_vars() => return ct.super_visit_with(self),
696+
_ => {}
625697
}
698+
ControlFlow::Continue(())
626699
}
627700
}

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)