From d26c5723e793c793c152fffaadc3dc7a45eac29e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 28 Dec 2023 18:51:53 +0100 Subject: [PATCH 1/2] Reject early-bound params in the type of assoc const bindings --- compiler/rustc_hir_analysis/messages.ftl | 18 +++ .../rustc_hir_analysis/src/astconv/bounds.rs | 108 ++++++++++++++++-- compiler/rustc_hir_analysis/src/errors.rs | 23 ++++ .../assoc-const-eq-param-in-ty.rs | 55 +++++++++ .../assoc-const-eq-param-in-ty.stderr | 76 ++++++++++++ 5 files changed, 270 insertions(+), 10 deletions(-) create mode 100644 tests/ui/associated-consts/assoc-const-eq-param-in-ty.rs create mode 100644 tests/ui/associated-consts/assoc-const-eq-param-in-ty.stderr diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index bad472dcb5c06..e38bcdcb298e1 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -316,6 +316,22 @@ hir_analysis_opaque_captures_higher_ranked_lifetime = `impl Trait` cannot captur .label = `impl Trait` implicitly captures all lifetimes in scope .note = lifetime declared here +hir_analysis_param_in_ty_of_assoc_const_binding = + the type of the associated constant `{$assoc_const}` must not depend on {$param_category -> + [self] `Self` + [synthetic] `impl Trait` + *[normal] generic parameters + } + .label = its type must not depend on {$param_category -> + [self] `Self` + [synthetic] `impl Trait` + *[normal] the {$param_def_kind} `{$param_name}` + } + .param_defined_here_label = {$param_category -> + [synthetic] the `impl Trait` is specified here + *[normal] the {$param_def_kind} `{$param_name}` is defined here + } + hir_analysis_paren_sugar_attribute = the `#[rustc_paren_sugar]` attribute is a temporary means of controlling which traits can use parenthetical notation .help = add `#![feature(unboxed_closures)]` to the crate attributes to use it @@ -431,6 +447,8 @@ hir_analysis_transparent_non_zero_sized_enum = the variant of a transparent {$de .label = needs at most one field with non-trivial size or alignment, but has {$field_count} .labels = this field has non-zero size or requires alignment +hir_analysis_ty_of_assoc_const_binding_note = `{$assoc_const}` has type `{$ty}` + hir_analysis_ty_param_first_local = type parameter `{$param_ty}` must be covered by another type when it appears before the first local type (`{$local_type}`) .label = type parameter `{$param_ty}` must be covered by another type when it appears before the first local type (`{$local_type}`) .note = implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type diff --git a/compiler/rustc_hir_analysis/src/astconv/bounds.rs b/compiler/rustc_hir_analysis/src/astconv/bounds.rs index 17e0aeb044cf8..e5cef88e324ae 100644 --- a/compiler/rustc_hir_analysis/src/astconv/bounds.rs +++ b/compiler/rustc_hir_analysis/src/astconv/bounds.rs @@ -1,12 +1,13 @@ -use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{codes::*, struct_span_code_err}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::ty::{self as ty, Ty}; +use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt}; use rustc_span::symbol::Ident; use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::traits; +use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; use smallvec::SmallVec; use crate::astconv::{AstConv, OnlySelfBounds, PredicateFilter}; @@ -438,14 +439,8 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { binding.kind { let ty = alias_ty.map_bound(|ty| tcx.type_of(ty.def_id).instantiate(tcx, ty.args)); - // Since the arguments passed to the alias type above may contain early-bound - // generic parameters, the instantiated type may contain some as well. - // Therefore wrap it in `EarlyBinder`. - // FIXME(fmease): Reject escaping late-bound vars. - tcx.feed_anon_const_type( - anon_const.def_id, - ty::EarlyBinder::bind(ty.skip_binder()), - ); + let ty = check_assoc_const_binding_type(tcx, assoc_ident, ty, binding.hir_id); + tcx.feed_anon_const_type(anon_const.def_id, ty::EarlyBinder::bind(ty)); } alias_ty @@ -537,3 +532,96 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { Ok(()) } } + +/// Detect and reject early-bound generic params in the type of associated const bindings. +/// +/// FIXME(const_generics): This is a temporary and semi-artifical restriction until the +/// arrival of *generic const generics*[^1]. +/// +/// It might actually be possible that we can already support early-bound generic params +/// in such types if we just lifted some more checks in other places, too, for example +/// inside [`ty::Const::from_anon_const`]. However, even if that were the case, we should +/// probably gate this behind another feature flag. +/// +/// [^1]: . +fn check_assoc_const_binding_type<'tcx>( + tcx: TyCtxt<'tcx>, + assoc_const: Ident, + ty: ty::Binder<'tcx, Ty<'tcx>>, + hir_id: hir::HirId, +) -> Ty<'tcx> { + // We can't perform the checks for early-bound params during name resolution unlike E0770 + // because this information depends on *type* resolution. + + // FIXME(fmease): Reject escaping late-bound vars. + let ty = ty.skip_binder(); + if !ty.has_param() { + return ty; + } + + let mut collector = GenericParamCollector { params: Default::default() }; + ty.visit_with(&mut collector); + + let mut guar = None; + let ty_note = ty + .make_suggestable(tcx, false) + .map(|ty| crate::errors::TyOfAssocConstBindingNote { assoc_const, ty }); + + let enclosing_item_owner_id = tcx + .hir() + .parent_owner_iter(hir_id) + .find_map(|(owner_id, parent)| parent.generics().map(|_| owner_id)) + .unwrap(); + let generics = tcx.generics_of(enclosing_item_owner_id); + for index in collector.params { + let param = generics.param_at(index as _, tcx); + let is_self_param = param.name == rustc_span::symbol::kw::SelfUpper; + guar.get_or_insert(tcx.dcx().emit_err(crate::errors::ParamInTyOfAssocConstBinding { + span: assoc_const.span, + assoc_const, + param_name: param.name, + param_def_kind: tcx.def_descr(param.def_id), + param_category: if is_self_param { + "self" + } else if param.kind.is_synthetic() { + "synthetic" + } else { + "normal" + }, + param_defined_here_label: + (!is_self_param).then(|| tcx.def_ident_span(param.def_id).unwrap()), + ty_note, + })); + } + + let guar = guar.unwrap_or_else(|| bug!("failed to find gen params in ty")); + Ty::new_error(tcx, guar) +} + +struct GenericParamCollector { + params: FxIndexSet, +} + +impl<'tcx> TypeVisitor> for GenericParamCollector { + fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { + if let ty::Param(param) = ty.kind() { + self.params.insert(param.index); + } else if ty.has_param() { + ty.super_visit_with(self) + } + } + + fn visit_region(&mut self, re: ty::Region<'tcx>) -> Self::Result { + if let ty::ReEarlyParam(param) = re.kind() { + self.params.insert(param.index); + } + } + + fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result { + if let ty::ConstKind::Param(param) = ct.kind() { + self.params.insert(param.index); + } else if ct.has_param() { + ct.super_visit_with(self) + } + } +} diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 175991b1be266..beec345109ed8 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -295,6 +295,29 @@ pub struct AssocTypeBindingNotAllowed { pub fn_trait_expansion: Option, } +#[derive(Diagnostic)] +#[diag(hir_analysis_param_in_ty_of_assoc_const_binding)] +pub(crate) struct ParamInTyOfAssocConstBinding<'tcx> { + #[primary_span] + #[label] + pub span: Span, + pub assoc_const: Ident, + pub param_name: Symbol, + pub param_def_kind: &'static str, + pub param_category: &'static str, + #[label(hir_analysis_param_defined_here_label)] + pub param_defined_here_label: Option, + #[subdiagnostic] + pub ty_note: Option>, +} + +#[derive(Subdiagnostic, Clone, Copy)] +#[note(hir_analysis_ty_of_assoc_const_binding_note)] +pub(crate) struct TyOfAssocConstBindingNote<'tcx> { + pub assoc_const: Ident, + pub ty: Ty<'tcx>, +} + #[derive(Subdiagnostic)] #[help(hir_analysis_parenthesized_fn_trait_expansion)] pub struct ParenthesizedFnTraitExpansion { diff --git a/tests/ui/associated-consts/assoc-const-eq-param-in-ty.rs b/tests/ui/associated-consts/assoc-const-eq-param-in-ty.rs new file mode 100644 index 0000000000000..aaf16181030b8 --- /dev/null +++ b/tests/ui/associated-consts/assoc-const-eq-param-in-ty.rs @@ -0,0 +1,55 @@ +// Regression test for issue #108271. +// Detect and reject generic params in the type of assoc consts used in an equality bound. +#![feature(associated_const_equality)] + +trait Trait<'a, T: 'a, const N: usize> { + const K: &'a [T; N]; +} + +fn take0<'r, A: 'r, const Q: usize>(_: impl Trait<'r, A, Q, K = { loop {} }>) {} +//~^ ERROR the type of the associated constant `K` must not depend on generic parameters +//~| NOTE its type must not depend on the lifetime parameter `'r` +//~| NOTE the lifetime parameter `'r` is defined here +//~| NOTE `K` has type `&'r [A; Q]` +//~| ERROR the type of the associated constant `K` must not depend on generic parameters +//~| NOTE its type must not depend on the type parameter `A` +//~| NOTE the type parameter `A` is defined here +//~| NOTE `K` has type `&'r [A; Q]` +//~| ERROR the type of the associated constant `K` must not depend on generic parameters +//~| NOTE its type must not depend on the const parameter `Q` +//~| NOTE the const parameter `Q` is defined here +//~| NOTE `K` has type `&'r [A; Q]` + +trait Project { + const SELF: Self; +} + +fn take1(_: impl Project) {} +//~^ ERROR the type of the associated constant `SELF` must not depend on `impl Trait` +//~| NOTE its type must not depend on `impl Trait` +//~| NOTE the `impl Trait` is specified here + +fn take2>(_: P) {} +//~^ ERROR the type of the associated constant `SELF` must not depend on generic parameters +//~| NOTE its type must not depend on the type parameter `P` +//~| NOTE the type parameter `P` is defined here +//~| NOTE `SELF` has type `P` + +trait Iface<'r> { + //~^ NOTE the lifetime parameter `'r` is defined here + type Assoc: Trait<'r, Self, Q, K = { loop {} }> + //~^ ERROR the type of the associated constant `K` must not depend on generic parameters + //~| NOTE its type must not depend on the lifetime parameter `'r` + //~| NOTE `K` has type `&'r [Self; Q]` + //~| ERROR the type of the associated constant `K` must not depend on `Self` + //~| NOTE its type must not depend on `Self` + //~| NOTE `K` has type `&'r [Self; Q]` + //~| ERROR the type of the associated constant `K` must not depend on generic parameters + //~| NOTE its type must not depend on the const parameter `Q` + //~| NOTE the const parameter `Q` is defined here + //~| NOTE `K` has type `&'r [Self; Q]` + where + Self: Sized + 'r; +} + +fn main() {} diff --git a/tests/ui/associated-consts/assoc-const-eq-param-in-ty.stderr b/tests/ui/associated-consts/assoc-const-eq-param-in-ty.stderr new file mode 100644 index 0000000000000..077ac6e7f9358 --- /dev/null +++ b/tests/ui/associated-consts/assoc-const-eq-param-in-ty.stderr @@ -0,0 +1,76 @@ +error: the type of the associated constant `K` must not depend on generic parameters + --> $DIR/assoc-const-eq-param-in-ty.rs:9:61 + | +LL | fn take0<'r, A: 'r, const Q: usize>(_: impl Trait<'r, A, Q, K = { loop {} }>) {} + | -- the lifetime parameter `'r` is defined here ^ its type must not depend on the lifetime parameter `'r` + | + = note: `K` has type `&'r [A; Q]` + +error: the type of the associated constant `K` must not depend on generic parameters + --> $DIR/assoc-const-eq-param-in-ty.rs:9:61 + | +LL | fn take0<'r, A: 'r, const Q: usize>(_: impl Trait<'r, A, Q, K = { loop {} }>) {} + | - the type parameter `A` is defined here ^ its type must not depend on the type parameter `A` + | + = note: `K` has type `&'r [A; Q]` + +error: the type of the associated constant `K` must not depend on generic parameters + --> $DIR/assoc-const-eq-param-in-ty.rs:9:61 + | +LL | fn take0<'r, A: 'r, const Q: usize>(_: impl Trait<'r, A, Q, K = { loop {} }>) {} + | - ^ its type must not depend on the const parameter `Q` + | | + | the const parameter `Q` is defined here + | + = note: `K` has type `&'r [A; Q]` + +error: the type of the associated constant `SELF` must not depend on `impl Trait` + --> $DIR/assoc-const-eq-param-in-ty.rs:27:26 + | +LL | fn take1(_: impl Project) {} + | -------------^^^^------ + | | | + | | its type must not depend on `impl Trait` + | the `impl Trait` is specified here + +error: the type of the associated constant `SELF` must not depend on generic parameters + --> $DIR/assoc-const-eq-param-in-ty.rs:32:21 + | +LL | fn take2>(_: P) {} + | - ^^^^ its type must not depend on the type parameter `P` + | | + | the type parameter `P` is defined here + | + = note: `SELF` has type `P` + +error: the type of the associated constant `K` must not depend on generic parameters + --> $DIR/assoc-const-eq-param-in-ty.rs:40:52 + | +LL | trait Iface<'r> { + | -- the lifetime parameter `'r` is defined here +LL | +LL | type Assoc: Trait<'r, Self, Q, K = { loop {} }> + | ^ its type must not depend on the lifetime parameter `'r` + | + = note: `K` has type `&'r [Self; Q]` + +error: the type of the associated constant `K` must not depend on `Self` + --> $DIR/assoc-const-eq-param-in-ty.rs:40:52 + | +LL | type Assoc: Trait<'r, Self, Q, K = { loop {} }> + | ^ its type must not depend on `Self` + | + = note: `K` has type `&'r [Self; Q]` + +error: the type of the associated constant `K` must not depend on generic parameters + --> $DIR/assoc-const-eq-param-in-ty.rs:40:52 + | +LL | type Assoc: Trait<'r, Self, Q, K = { loop {} }> + | - ^ its type must not depend on the const parameter `Q` + | | + | the const parameter `Q` is defined here + | + = note: `K` has type `&'r [Self; Q]` + +error: aborting due to 8 previous errors + From 0b2fb8db6540b0548230cd335e7b2a845686f7ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 10 Jan 2024 02:11:25 +0100 Subject: [PATCH 2/2] Reject escaping bound vars in the type of assoc const bindings --- compiler/rustc_hir_analysis/messages.ftl | 5 + .../rustc_hir_analysis/src/astconv/bounds.rs | 113 ++++++++++++++---- compiler/rustc_hir_analysis/src/errors.rs | 15 +++ .../assoc-const-eq-bound-var-in-ty-not-wf.rs | 25 ++++ ...soc-const-eq-bound-var-in-ty-not-wf.stderr | 25 ++++ .../assoc-const-eq-bound-var-in-ty.rs | 22 ++++ .../assoc-const-eq-esc-bound-var-in-ty.rs | 15 +++ .../assoc-const-eq-esc-bound-var-in-ty.stderr | 12 ++ 8 files changed, 212 insertions(+), 20 deletions(-) create mode 100644 tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty-not-wf.rs create mode 100644 tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty-not-wf.stderr create mode 100644 tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty.rs create mode 100644 tests/ui/associated-consts/assoc-const-eq-esc-bound-var-in-ty.rs create mode 100644 tests/ui/associated-consts/assoc-const-eq-esc-bound-var-in-ty.stderr diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index e38bcdcb298e1..364e39e626bf6 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -118,6 +118,11 @@ hir_analysis_enum_discriminant_overflowed = enum discriminant overflowed .label = overflowed on value after {$discr} .note = explicitly set `{$item_name} = {$wrapped_discr}` if that is desired outcome +hir_analysis_escaping_bound_var_in_ty_of_assoc_const_binding = + the type of the associated constant `{$assoc_const}` cannot capture late-bound generic parameters + .label = its type cannot capture the late-bound {$var_def_kind} `{$var_name}` + .var_defined_here_label = the late-bound {$var_def_kind} `{$var_name}` is defined here + hir_analysis_field_already_declared = field `{$field_name}` is already declared .label = field already declared diff --git a/compiler/rustc_hir_analysis/src/astconv/bounds.rs b/compiler/rustc_hir_analysis/src/astconv/bounds.rs index e5cef88e324ae..ff365d6275a92 100644 --- a/compiler/rustc_hir_analysis/src/astconv/bounds.rs +++ b/compiler/rustc_hir_analysis/src/astconv/bounds.rs @@ -1,3 +1,5 @@ +use std::ops::ControlFlow; + use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{codes::*, struct_span_code_err}; use rustc_hir as hir; @@ -5,7 +7,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt}; use rustc_span::symbol::Ident; -use rustc_span::{ErrorGuaranteed, Span}; +use rustc_span::{ErrorGuaranteed, Span, Symbol}; use rustc_trait_selection::traits; use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; use smallvec::SmallVec; @@ -533,7 +535,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } } -/// Detect and reject early-bound generic params in the type of associated const bindings. +/// Detect and reject early-bound & escaping late-bound generic params in the type of assoc const bindings. /// /// FIXME(const_generics): This is a temporary and semi-artifical restriction until the /// arrival of *generic const generics*[^1]. @@ -552,17 +554,23 @@ fn check_assoc_const_binding_type<'tcx>( ) -> Ty<'tcx> { // We can't perform the checks for early-bound params during name resolution unlike E0770 // because this information depends on *type* resolution. + // We can't perform these checks in `resolve_bound_vars` either for the same reason. + // Consider the trait ref `for<'a> Trait<'a, C = { &0 }>`. We need to know the fully + // resolved type of `Trait::C` in order to know if it references `'a` or not. - // FIXME(fmease): Reject escaping late-bound vars. let ty = ty.skip_binder(); - if !ty.has_param() { + if !ty.has_param() && !ty.has_escaping_bound_vars() { return ty; } - let mut collector = GenericParamCollector { params: Default::default() }; - ty.visit_with(&mut collector); + let mut collector = GenericParamAndBoundVarCollector { + tcx, + params: Default::default(), + vars: Default::default(), + depth: ty::INNERMOST, + }; + let mut guar = ty.visit_with(&mut collector).break_value(); - let mut guar = None; let ty_note = ty .make_suggestable(tcx, false) .map(|ty| crate::errors::TyOfAssocConstBindingNote { assoc_const, ty }); @@ -593,35 +601,100 @@ fn check_assoc_const_binding_type<'tcx>( ty_note, })); } + for (var_def_id, var_name) in collector.vars { + guar.get_or_insert(tcx.dcx().emit_err( + crate::errors::EscapingBoundVarInTyOfAssocConstBinding { + span: assoc_const.span, + assoc_const, + var_name, + var_def_kind: tcx.def_descr(var_def_id), + var_defined_here_label: tcx.def_ident_span(var_def_id).unwrap(), + ty_note, + }, + )); + } - let guar = guar.unwrap_or_else(|| bug!("failed to find gen params in ty")); + let guar = guar.unwrap_or_else(|| bug!("failed to find gen params or bound vars in ty")); Ty::new_error(tcx, guar) } -struct GenericParamCollector { +struct GenericParamAndBoundVarCollector<'tcx> { + tcx: TyCtxt<'tcx>, params: FxIndexSet, + vars: FxIndexSet<(DefId, Symbol)>, + depth: ty::DebruijnIndex, } -impl<'tcx> TypeVisitor> for GenericParamCollector { +impl<'tcx> TypeVisitor> for GenericParamAndBoundVarCollector<'tcx> { + type Result = ControlFlow; + + fn visit_binder>>( + &mut self, + binder: &ty::Binder<'tcx, T>, + ) -> Self::Result { + self.depth.shift_in(1); + let result = binder.super_visit_with(self); + self.depth.shift_out(1); + result + } + fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { - if let ty::Param(param) = ty.kind() { - self.params.insert(param.index); - } else if ty.has_param() { - ty.super_visit_with(self) + match ty.kind() { + ty::Param(param) => { + self.params.insert(param.index); + } + ty::Bound(db, bt) if *db >= self.depth => { + self.vars.insert(match bt.kind { + ty::BoundTyKind::Param(def_id, name) => (def_id, name), + ty::BoundTyKind::Anon => { + let reported = self + .tcx + .dcx() + .delayed_bug(format!("unexpected anon bound ty: {:?}", bt.var)); + return ControlFlow::Break(reported); + } + }); + } + _ if ty.has_param() || ty.has_bound_vars() => return ty.super_visit_with(self), + _ => {} } + ControlFlow::Continue(()) } fn visit_region(&mut self, re: ty::Region<'tcx>) -> Self::Result { - if let ty::ReEarlyParam(param) = re.kind() { - self.params.insert(param.index); + match re.kind() { + ty::ReEarlyParam(param) => { + self.params.insert(param.index); + } + ty::ReBound(db, br) if db >= self.depth => { + self.vars.insert(match br.kind { + ty::BrNamed(def_id, name) => (def_id, name), + ty::BrAnon | ty::BrEnv => { + let guar = self + .tcx + .dcx() + .delayed_bug(format!("unexpected bound region kind: {:?}", br.kind)); + return ControlFlow::Break(guar); + } + }); + } + _ => {} } + ControlFlow::Continue(()) } fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result { - if let ty::ConstKind::Param(param) = ct.kind() { - self.params.insert(param.index); - } else if ct.has_param() { - ct.super_visit_with(self) + match ct.kind() { + ty::ConstKind::Param(param) => { + self.params.insert(param.index); + } + ty::ConstKind::Bound(db, ty::BoundVar { .. }) if db >= self.depth => { + let guar = self.tcx.dcx().delayed_bug("unexpected escaping late-bound const var"); + return ControlFlow::Break(guar); + } + _ if ct.has_param() || ct.has_bound_vars() => return ct.super_visit_with(self), + _ => {} } + ControlFlow::Continue(()) } } diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index beec345109ed8..dc0e1ae6d87c5 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -318,6 +318,21 @@ pub(crate) struct TyOfAssocConstBindingNote<'tcx> { pub ty: Ty<'tcx>, } +#[derive(Diagnostic)] +#[diag(hir_analysis_escaping_bound_var_in_ty_of_assoc_const_binding)] +pub(crate) struct EscapingBoundVarInTyOfAssocConstBinding<'tcx> { + #[primary_span] + #[label] + pub span: Span, + pub assoc_const: Ident, + pub var_name: Symbol, + pub var_def_kind: &'static str, + #[label(hir_analysis_var_defined_here_label)] + pub var_defined_here_label: Span, + #[subdiagnostic] + pub ty_note: Option>, +} + #[derive(Subdiagnostic)] #[help(hir_analysis_parenthesized_fn_trait_expansion)] pub struct ParenthesizedFnTraitExpansion { diff --git a/tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty-not-wf.rs b/tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty-not-wf.rs new file mode 100644 index 0000000000000..a718eb23bed59 --- /dev/null +++ b/tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty-not-wf.rs @@ -0,0 +1,25 @@ +// Check that we eventually catch types of assoc const bounds +// (containing late-bound vars) that are ill-formed. +#![feature(associated_const_equality)] + +trait Trait { + const K: T; +} + +fn take( + _: impl Trait< + < fn(&'a str) -> &'a str as Project>::Out as Discard>::Out, + K = { () } + >, +) {} +//~^^^^^^ ERROR implementation of `Project` is not general enough +//~^^^^ ERROR higher-ranked subtype error +//~| ERROR higher-ranked subtype error + +trait Project { type Out; } +impl Project for fn(T) -> T { type Out = T; } + +trait Discard { type Out; } +impl Discard for T { type Out = (); } + +fn main() {} diff --git a/tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty-not-wf.stderr b/tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty-not-wf.stderr new file mode 100644 index 0000000000000..967814c9c3d9d --- /dev/null +++ b/tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty-not-wf.stderr @@ -0,0 +1,25 @@ +error: higher-ranked subtype error + --> $DIR/assoc-const-eq-bound-var-in-ty-not-wf.rs:12:13 + | +LL | K = { () } + | ^^^^^^ + +error: higher-ranked subtype error + --> $DIR/assoc-const-eq-bound-var-in-ty-not-wf.rs:12:13 + | +LL | K = { () } + | ^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: implementation of `Project` is not general enough + --> $DIR/assoc-const-eq-bound-var-in-ty-not-wf.rs:9:4 + | +LL | fn take( + | ^^^^ implementation of `Project` is not general enough + | + = note: `Project` would have to be implemented for the type `for<'a> fn(&'a str) -> &'a str` + = note: ...but `Project` is actually implemented for the type `fn(&'0 str) -> &'0 str`, for some specific lifetime `'0` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty.rs b/tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty.rs new file mode 100644 index 0000000000000..7fc6d564ca444 --- /dev/null +++ b/tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty.rs @@ -0,0 +1,22 @@ +// Check that we don't reject non-escaping late-bound vars in the type of assoc const bindings. +// There's no reason why we should disallow them. +// +//@ check-pass + +#![feature(associated_const_equality)] + +trait Trait { + const K: T; +} + +fn take( + _: impl Trait< + fn(&'a str) -> &'a str as Discard>::Out, + K = { () } + >, +) {} + +trait Discard { type Out; } +impl Discard for T { type Out = (); } + +fn main() {} diff --git a/tests/ui/associated-consts/assoc-const-eq-esc-bound-var-in-ty.rs b/tests/ui/associated-consts/assoc-const-eq-esc-bound-var-in-ty.rs new file mode 100644 index 0000000000000..6db1e85ccfa6a --- /dev/null +++ b/tests/ui/associated-consts/assoc-const-eq-esc-bound-var-in-ty.rs @@ -0,0 +1,15 @@ +// Detect and reject escaping late-bound generic params in +// the type of assoc consts used in an equality bound. +#![feature(associated_const_equality)] + +trait Trait<'a> { + const K: &'a (); +} + +fn take(_: impl for<'r> Trait<'r, K = { &() }>) {} +//~^ ERROR the type of the associated constant `K` cannot capture late-bound generic parameters +//~| NOTE its type cannot capture the late-bound lifetime parameter `'r` +//~| NOTE the late-bound lifetime parameter `'r` is defined here +//~| NOTE `K` has type `&'r ()` + +fn main() {} diff --git a/tests/ui/associated-consts/assoc-const-eq-esc-bound-var-in-ty.stderr b/tests/ui/associated-consts/assoc-const-eq-esc-bound-var-in-ty.stderr new file mode 100644 index 0000000000000..349fddcafe8b7 --- /dev/null +++ b/tests/ui/associated-consts/assoc-const-eq-esc-bound-var-in-ty.stderr @@ -0,0 +1,12 @@ +error: the type of the associated constant `K` cannot capture late-bound generic parameters + --> $DIR/assoc-const-eq-esc-bound-var-in-ty.rs:9:35 + | +LL | fn take(_: impl for<'r> Trait<'r, K = { &() }>) {} + | -- ^ its type cannot capture the late-bound lifetime parameter `'r` + | | + | the late-bound lifetime parameter `'r` is defined here + | + = note: `K` has type `&'r ()` + +error: aborting due to 1 previous error +