Auto merge of #135186 - camelid:const-path-multi, r=BoxyUwU

mgca: Lower all const paths as `ConstArgKind::Path`

When `#![feature(min_generic_const_args)]` is enabled, we now lower all
const paths in generic arg position to `hir::ConstArgKind::Path`. We
then lower assoc const paths to `ty::ConstKind::Unevaluated` since we
can no longer use the anon const expression lowering machinery. In the
process of implementing this, I factored out `hir_ty_lowering` code that
is now shared between lowering assoc types and assoc consts.

This PR also introduces a `#[type_const]` attribute for trait assoc
consts that are allowed as const args. However, we still need to
implement code to check that assoc const definitions satisfy
`#[type_const]` if present (basically is it a const path or a
monomorphic anon const).

r? `@BoxyUwU`
This commit is contained in:
bors 2025-03-04 22:37:37 +00:00
commit 08db600e8e
35 changed files with 602 additions and 148 deletions

View file

@ -124,10 +124,19 @@ impl Path {
self.segments.first().is_some_and(|segment| segment.ident.name == kw::PathRoot) self.segments.first().is_some_and(|segment| segment.ident.name == kw::PathRoot)
} }
/// If this path is a single identifier with no arguments, does not ensure /// Check if this path is potentially a trivial const arg, i.e., one that can _potentially_
/// that the path resolves to a const param, the caller should check this. /// be represented without an anon const in the HIR.
pub fn is_potential_trivial_const_arg(&self) -> bool { ///
matches!(self.segments[..], [PathSegment { args: None, .. }]) /// If `allow_mgca_arg` is true (as should be the case in most situations when
/// `#![feature(min_generic_const_args)]` is enabled), then this always returns true
/// because all paths are valid.
///
/// Otherwise, it returns true iff the path has exactly one segment, and it has no generic args
/// (i.e., it is _potentially_ a const parameter).
#[tracing::instrument(level = "debug", ret)]
pub fn is_potential_trivial_const_arg(&self, allow_mgca_arg: bool) -> bool {
allow_mgca_arg
|| self.segments.len() == 1 && self.segments.iter().all(|seg| seg.args.is_none())
} }
} }
@ -1208,24 +1217,33 @@ pub struct Expr {
} }
impl Expr { impl Expr {
/// Could this expr be either `N`, or `{ N }`, where `N` is a const parameter. /// Check if this expression is potentially a trivial const arg, i.e., one that can _potentially_
/// be represented without an anon const in the HIR.
/// ///
/// If this is not the case, name resolution does not resolve `N` when using /// This will unwrap at most one block level (curly braces). After that, if the expression
/// `min_const_generics` as more complex expressions are not supported. /// is a path, it mostly dispatches to [`Path::is_potential_trivial_const_arg`].
/// See there for more info about `allow_mgca_arg`.
/// ///
/// Does not ensure that the path resolves to a const param, the caller should check this. /// The only additional thing to note is that when `allow_mgca_arg` is false, this function
/// will only allow paths with no qself, before dispatching to the `Path` function of
/// the same name.
///
/// Does not ensure that the path resolves to a const param/item, the caller should check this.
/// This also does not consider macros, so it's only correct after macro-expansion. /// This also does not consider macros, so it's only correct after macro-expansion.
pub fn is_potential_trivial_const_arg(&self) -> bool { pub fn is_potential_trivial_const_arg(&self, allow_mgca_arg: bool) -> bool {
let this = self.maybe_unwrap_block(); let this = self.maybe_unwrap_block();
if allow_mgca_arg {
matches!(this.kind, ExprKind::Path(..))
} else {
if let ExprKind::Path(None, path) = &this.kind if let ExprKind::Path(None, path) = &this.kind
&& path.is_potential_trivial_const_arg() && path.is_potential_trivial_const_arg(allow_mgca_arg)
{ {
true true
} else { } else {
false false
} }
} }
}
/// Returns an expression with (when possible) *one* outter brace removed /// Returns an expression with (when possible) *one* outter brace removed
pub fn maybe_unwrap_block(&self) -> &Expr { pub fn maybe_unwrap_block(&self) -> &Expr {

View file

@ -1094,7 +1094,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
.and_then(|partial_res| partial_res.full_res()) .and_then(|partial_res| partial_res.full_res())
{ {
if !res.matches_ns(Namespace::TypeNS) if !res.matches_ns(Namespace::TypeNS)
&& path.is_potential_trivial_const_arg() && path.is_potential_trivial_const_arg(false)
{ {
debug!( debug!(
"lower_generic_arg: Lowering type argument as const argument: {:?}", "lower_generic_arg: Lowering type argument as const argument: {:?}",
@ -2061,8 +2061,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
) -> &'hir hir::ConstArg<'hir> { ) -> &'hir hir::ConstArg<'hir> {
let tcx = self.tcx; let tcx = self.tcx;
// FIXME(min_generic_const_args): we only allow one-segment const paths for now let ct_kind = if path
let ct_kind = if path.is_potential_trivial_const_arg() .is_potential_trivial_const_arg(tcx.features().min_generic_const_args())
&& (tcx.features().min_generic_const_args() && (tcx.features().min_generic_const_args()
|| matches!(res, Res::Def(DefKind::ConstParam, _))) || matches!(res, Res::Def(DefKind::ConstParam, _)))
{ {
@ -2072,7 +2072,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
path, path,
ParamMode::Optional, ParamMode::Optional,
AllowReturnTypeNotation::No, AllowReturnTypeNotation::No,
// FIXME(min_generic_const_args): update for `fn foo() -> Bar<FOO<impl Trait>>` support // FIXME(mgca): update for `fn foo() -> Bar<FOO<impl Trait>>` support
ImplTraitContext::Disallowed(ImplTraitPosition::Path), ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None, None,
); );
@ -2136,19 +2136,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}; };
let maybe_res = let maybe_res =
self.resolver.get_partial_res(expr.id).and_then(|partial_res| partial_res.full_res()); self.resolver.get_partial_res(expr.id).and_then(|partial_res| partial_res.full_res());
// FIXME(min_generic_const_args): we only allow one-segment const paths for now if let ExprKind::Path(qself, path) = &expr.kind
if let ExprKind::Path(None, path) = &expr.kind && path.is_potential_trivial_const_arg(tcx.features().min_generic_const_args())
&& path.is_potential_trivial_const_arg()
&& (tcx.features().min_generic_const_args() && (tcx.features().min_generic_const_args()
|| matches!(maybe_res, Some(Res::Def(DefKind::ConstParam, _)))) || matches!(maybe_res, Some(Res::Def(DefKind::ConstParam, _))))
{ {
let qpath = self.lower_qpath( let qpath = self.lower_qpath(
expr.id, expr.id,
&None, qself,
path, path,
ParamMode::Optional, ParamMode::Optional,
AllowReturnTypeNotation::No, AllowReturnTypeNotation::No,
// FIXME(min_generic_const_args): update for `fn foo() -> Bar<FOO<impl Trait>>` support // FIXME(mgca): update for `fn foo() -> Bar<FOO<impl Trait>>` support
ImplTraitContext::Disallowed(ImplTraitPosition::Path), ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None, None,
); );

View file

@ -190,7 +190,8 @@ fn make_format_args(
&& let [stmt] = block.stmts.as_slice() && let [stmt] = block.stmts.as_slice()
&& let StmtKind::Expr(expr) = &stmt.kind && let StmtKind::Expr(expr) = &stmt.kind
&& let ExprKind::Path(None, path) = &expr.kind && let ExprKind::Path(None, path) = &expr.kind
&& path.is_potential_trivial_const_arg() && path.segments.len() == 1
&& path.segments[0].args.is_none()
{ {
err.multipart_suggestion( err.multipart_suggestion(
"quote your inlined format argument to use as string literal", "quote your inlined format argument to use as string literal",

View file

@ -576,6 +576,13 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
EncodeCrossCrate::Yes, experimental!(patchable_function_entry) EncodeCrossCrate::Yes, experimental!(patchable_function_entry)
), ),
// Probably temporary component of min_generic_const_args.
// `#[type_const] const ASSOC: usize;`
gated!(
type_const, Normal, template!(Word), ErrorFollowing,
EncodeCrossCrate::Yes, min_generic_const_args, experimental!(type_const),
),
// ========================================================================== // ==========================================================================
// Internal attributes: Stability, deprecation, and unsafe: // Internal attributes: Stability, deprecation, and unsafe:
// ========================================================================== // ==========================================================================

View file

@ -45,6 +45,7 @@ use tracing::{debug, instrument};
use crate::check::intrinsic::intrinsic_operation_unsafety; use crate::check::intrinsic::intrinsic_operation_unsafety;
use crate::errors; use crate::errors;
use crate::hir_ty_lowering::errors::assoc_kind_str;
use crate::hir_ty_lowering::{FeedConstTy, HirTyLowerer, RegionInferReason}; use crate::hir_ty_lowering::{FeedConstTy, HirTyLowerer, RegionInferReason};
pub(crate) mod dump; pub(crate) mod dump;
@ -443,13 +444,14 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
self.tcx.at(span).type_param_predicates((self.item_def_id, def_id, assoc_name)) self.tcx.at(span).type_param_predicates((self.item_def_id, def_id, assoc_name))
} }
fn lower_assoc_ty( fn lower_assoc_shared(
&self, &self,
span: Span, span: Span,
item_def_id: DefId, item_def_id: DefId,
item_segment: &hir::PathSegment<'tcx>, item_segment: &rustc_hir::PathSegment<'tcx>,
poly_trait_ref: ty::PolyTraitRef<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>,
) -> Ty<'tcx> { kind: ty::AssocKind,
) -> Result<(DefId, ty::GenericArgsRef<'tcx>), ErrorGuaranteed> {
if let Some(trait_ref) = poly_trait_ref.no_bound_vars() { if let Some(trait_ref) = poly_trait_ref.no_bound_vars() {
let item_args = self.lowerer().lower_generic_args_of_assoc_item( let item_args = self.lowerer().lower_generic_args_of_assoc_item(
span, span,
@ -457,7 +459,7 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
item_segment, item_segment,
trait_ref.args, trait_ref.args,
); );
Ty::new_projection_from_args(self.tcx(), item_def_id, item_args) Ok((item_def_id, item_args))
} else { } else {
// There are no late-bound regions; we can just ignore the binder. // There are no late-bound regions; we can just ignore the binder.
let (mut mpart_sugg, mut inferred_sugg) = (None, None); let (mut mpart_sugg, mut inferred_sugg) = (None, None);
@ -518,16 +520,14 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
} }
_ => {} _ => {}
} }
Ty::new_error(
self.tcx(), Err(self.tcx().dcx().emit_err(errors::AssociatedItemTraitUninferredGenericParams {
self.tcx().dcx().emit_err(errors::AssociatedItemTraitUninferredGenericParams {
span, span,
inferred_sugg, inferred_sugg,
bound, bound,
mpart_sugg, mpart_sugg,
what: "type", what: assoc_kind_str(kind),
}), }))
)
} }
} }

View file

@ -468,11 +468,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// Good error for `where Trait::method(..): Send`. // Good error for `where Trait::method(..): Send`.
let Some(self_ty) = opt_self_ty else { let Some(self_ty) = opt_self_ty else {
return self.error_missing_qpath_self_ty( let guar = self.error_missing_qpath_self_ty(
trait_def_id, trait_def_id,
hir_ty.span, hir_ty.span,
item_segment, item_segment,
ty::AssocKind::Type,
); );
return Ty::new_error(tcx, guar);
}; };
let self_ty = self.lower_ty(self_ty); let self_ty = self.lower_ty(self_ty);

View file

@ -385,14 +385,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}) })
} }
pub(super) fn report_ambiguous_assoc_ty( pub(super) fn report_ambiguous_assoc(
&self, &self,
span: Span, span: Span,
types: &[String], types: &[String],
traits: &[String], traits: &[String],
name: Symbol, name: Symbol,
kind: ty::AssocKind,
) -> ErrorGuaranteed { ) -> ErrorGuaranteed {
let mut err = struct_span_code_err!(self.dcx(), span, E0223, "ambiguous associated type"); let kind_str = assoc_kind_str(kind);
let mut err =
struct_span_code_err!(self.dcx(), span, E0223, "ambiguous associated {kind_str}");
if self if self
.tcx() .tcx()
.resolutions(()) .resolutions(())
@ -417,7 +420,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
span, span,
format!( format!(
"if there were a type named `Type` that implements a trait named \ "if there were a type named `Type` that implements a trait named \
`Trait` with associated type `{name}`, you could use the \ `Trait` with associated {kind_str} `{name}`, you could use the \
fully-qualified path", fully-qualified path",
), ),
format!("<Type as Trait>::{name}"), format!("<Type as Trait>::{name}"),
@ -440,7 +443,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
span, span,
format!( format!(
"if there were a type named `Example` that implemented one of the \ "if there were a type named `Example` that implemented one of the \
traits with associated type `{name}`, you could use the \ traits with associated {kind_str} `{name}`, you could use the \
fully-qualified path", fully-qualified path",
), ),
traits.iter().map(|trait_str| format!("<Example as {trait_str}>::{name}")), traits.iter().map(|trait_str| format!("<Example as {trait_str}>::{name}")),
@ -451,7 +454,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
err.span_suggestion_verbose( err.span_suggestion_verbose(
span, span,
format!( format!(
"if there were a trait named `Example` with associated type `{name}` \ "if there were a trait named `Example` with associated {kind_str} `{name}` \
implemented for `{type_str}`, you could use the fully-qualified path", implemented for `{type_str}`, you could use the fully-qualified path",
), ),
format!("<{type_str} as Example>::{name}"), format!("<{type_str} as Example>::{name}"),
@ -462,7 +465,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
err.span_suggestions( err.span_suggestions(
span, span,
format!( format!(
"if there were a trait named `Example` with associated type `{name}` \ "if there were a trait named `Example` with associated {kind_str} `{name}` \
implemented for one of the types, you could use the fully-qualified \ implemented for one of the types, you could use the fully-qualified \
path", path",
), ),
@ -491,7 +494,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
err.emit() err.emit()
} }
pub(crate) fn complain_about_ambiguous_inherent_assoc_ty( pub(crate) fn complain_about_ambiguous_inherent_assoc(
&self, &self,
name: Ident, name: Ident,
candidates: Vec<DefId>, candidates: Vec<DefId>,
@ -552,13 +555,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
} }
// FIXME(inherent_associated_types): Find similarly named associated types and suggest them. // FIXME(inherent_associated_types): Find similarly named associated types and suggest them.
pub(crate) fn complain_about_inherent_assoc_ty_not_found( pub(crate) fn complain_about_inherent_assoc_not_found(
&self, &self,
name: Ident, name: Ident,
self_ty: Ty<'tcx>, self_ty: Ty<'tcx>,
candidates: Vec<(DefId, (DefId, DefId))>, candidates: Vec<(DefId, (DefId, DefId))>,
fulfillment_errors: Vec<FulfillmentError<'tcx>>, fulfillment_errors: Vec<FulfillmentError<'tcx>>,
span: Span, span: Span,
kind: ty::AssocKind,
) -> ErrorGuaranteed { ) -> ErrorGuaranteed {
// FIXME(fmease): This was copied in parts from an old version of `rustc_hir_typeck::method::suggest`. // FIXME(fmease): This was copied in parts from an old version of `rustc_hir_typeck::method::suggest`.
// Either // Either
@ -568,12 +572,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let tcx = self.tcx(); let tcx = self.tcx();
let kind_str = assoc_kind_str(kind);
let adt_did = self_ty.ty_adt_def().map(|def| def.did()); let adt_did = self_ty.ty_adt_def().map(|def| def.did());
let add_def_label = |err: &mut Diag<'_>| { let add_def_label = |err: &mut Diag<'_>| {
if let Some(did) = adt_did { if let Some(did) = adt_did {
err.span_label( err.span_label(
tcx.def_span(did), tcx.def_span(did),
format!("associated item `{name}` not found for this {}", tcx.def_descr(did)), format!(
"associated {kind_str} `{name}` not found for this {}",
tcx.def_descr(did)
),
); );
} }
}; };
@ -600,11 +608,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
self.dcx(), self.dcx(),
name.span, name.span,
E0220, E0220,
"associated type `{name}` not found for `{self_ty}` in the current scope" "associated {kind_str} `{name}` not found for `{self_ty}` in the current scope"
); );
err.span_label(name.span, format!("associated item not found in `{self_ty}`")); err.span_label(name.span, format!("associated item not found in `{self_ty}`"));
err.note(format!( err.note(format!(
"the associated type was found for\n{type_candidates}{additional_types}", "the associated {kind_str} was found for\n{type_candidates}{additional_types}",
)); ));
add_def_label(&mut err); add_def_label(&mut err);
return err.emit(); return err.emit();
@ -685,7 +693,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let mut err = self.dcx().struct_span_err( let mut err = self.dcx().struct_span_err(
name.span, name.span,
format!("the associated type `{name}` exists for `{self_ty}`, but its trait bounds were not satisfied") format!("the associated {kind_str} `{name}` exists for `{self_ty}`, but its trait bounds were not satisfied")
); );
if !bounds.is_empty() { if !bounds.is_empty() {
err.note(format!( err.note(format!(
@ -695,7 +703,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
} }
err.span_label( err.span_label(
name.span, name.span,
format!("associated type cannot be referenced on `{self_ty}` due to unsatisfied trait bounds") format!("associated {kind_str} cannot be referenced on `{self_ty}` due to unsatisfied trait bounds")
); );
for (span, mut bounds) in bound_spans { for (span, mut bounds) in bound_spans {
@ -1614,7 +1622,7 @@ fn generics_args_err_extend<'a>(
} }
} }
pub(super) fn assoc_kind_str(kind: ty::AssocKind) -> &'static str { pub(crate) fn assoc_kind_str(kind: ty::AssocKind) -> &'static str {
match kind { match kind {
ty::AssocKind::Fn => "function", ty::AssocKind::Fn => "function",
ty::AssocKind::Const => "constant", ty::AssocKind::Const => "constant",

View file

@ -115,7 +115,7 @@ fn generic_arg_mismatch_err(
body.value.kind body.value.kind
&& let Res::Def(DefKind::Fn { .. }, id) = path.res && let Res::Def(DefKind::Fn { .. }, id) = path.res
{ {
// FIXME(min_generic_const_args): this branch is dead once new const path lowering // FIXME(mgca): this branch is dead once new const path lowering
// (for single-segment paths) is no longer gated // (for single-segment paths) is no longer gated
err.help(format!("`{}` is a function item, not a type", tcx.item_name(id))); err.help(format!("`{}` is a function item, not a type", tcx.item_name(id)));
err.help("function item types cannot be named directly"); err.help("function item types cannot be named directly");

View file

@ -44,14 +44,16 @@ use rustc_middle::ty::{
}; };
use rustc_middle::{bug, span_bug}; use rustc_middle::{bug, span_bug};
use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS; use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS;
use rustc_session::parse::feature_err;
use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw}; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::wf::object_region_bounds; use rustc_trait_selection::traits::wf::object_region_bounds;
use rustc_trait_selection::traits::{self, ObligationCtxt}; use rustc_trait_selection::traits::{self, ObligationCtxt};
use rustc_type_ir::Upcast; use rustc_type_ir::Upcast;
use tracing::{debug, instrument}; use tracing::{debug, instrument};
use self::errors::assoc_kind_str;
use crate::check::check_abi_fn_ptr; use crate::check::check_abi_fn_ptr;
use crate::errors::{ use crate::errors::{
AmbiguousLifetimeBound, BadReturnTypeNotation, InvalidBaseType, NoVariantNamed, AmbiguousLifetimeBound, BadReturnTypeNotation, InvalidBaseType, NoVariantNamed,
@ -152,7 +154,7 @@ pub trait HirTyLowerer<'tcx> {
assoc_name: Ident, assoc_name: Ident,
) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]>; ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]>;
/// Lower an associated type to a projection. /// Lower an associated type/const (from a trait) to a projection.
/// ///
/// This method has to be defined by the concrete lowering context because /// This method has to be defined by the concrete lowering context because
/// dealing with higher-ranked trait references depends on its capabilities: /// dealing with higher-ranked trait references depends on its capabilities:
@ -164,13 +166,14 @@ pub trait HirTyLowerer<'tcx> {
/// ///
/// The canonical example of this is associated type `T::P` where `T` is a type /// The canonical example of this is associated type `T::P` where `T` is a type
/// param constrained by `T: for<'a> Trait<'a>` and where `Trait` defines `P`. /// param constrained by `T: for<'a> Trait<'a>` and where `Trait` defines `P`.
fn lower_assoc_ty( fn lower_assoc_shared(
&self, &self,
span: Span, span: Span,
item_def_id: DefId, item_def_id: DefId,
item_segment: &hir::PathSegment<'tcx>, item_segment: &hir::PathSegment<'tcx>,
poly_trait_ref: ty::PolyTraitRef<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>,
) -> Ty<'tcx>; kind: ty::AssocKind,
) -> Result<(DefId, GenericArgsRef<'tcx>), ErrorGuaranteed>;
fn lower_fn_sig( fn lower_fn_sig(
&self, &self,
@ -245,6 +248,42 @@ pub enum FeedConstTy<'a, 'tcx> {
No, No,
} }
#[derive(Debug, Clone, Copy)]
enum LowerAssocMode {
Type { permit_variants: bool },
Const,
}
impl LowerAssocMode {
fn kind(self) -> ty::AssocKind {
match self {
LowerAssocMode::Type { .. } => ty::AssocKind::Type,
LowerAssocMode::Const => ty::AssocKind::Const,
}
}
fn def_kind(self) -> DefKind {
match self {
LowerAssocMode::Type { .. } => DefKind::AssocTy,
LowerAssocMode::Const => DefKind::AssocConst,
}
}
fn permit_variants(self) -> bool {
match self {
LowerAssocMode::Type { permit_variants } => permit_variants,
// FIXME(mgca): Support paths like `Option::<T>::None` or `Option::<T>::Some` which resolve to const ctors/fn items respectively
LowerAssocMode::Const => false,
}
}
}
#[derive(Debug, Clone, Copy)]
enum LoweredAssoc<'tcx> {
Term(DefId, GenericArgsRef<'tcx>),
Variant { adt: Ty<'tcx>, variant_did: DefId },
}
/// New-typed boolean indicating whether explicit late-bound lifetimes /// New-typed boolean indicating whether explicit late-bound lifetimes
/// are present in a set of generic arguments. /// are present in a set of generic arguments.
/// ///
@ -630,7 +669,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
(args, arg_count) (args, arg_count)
} }
#[instrument(level = "debug", skip_all)] #[instrument(level = "debug", skip(self))]
pub fn lower_generic_args_of_assoc_item( pub fn lower_generic_args_of_assoc_item(
&self, &self,
span: Span, span: Span,
@ -638,7 +677,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
item_segment: &hir::PathSegment<'tcx>, item_segment: &hir::PathSegment<'tcx>,
parent_args: GenericArgsRef<'tcx>, parent_args: GenericArgsRef<'tcx>,
) -> GenericArgsRef<'tcx> { ) -> GenericArgsRef<'tcx> {
debug!(?span, ?item_def_id, ?item_segment);
let (args, _) = let (args, _) =
self.lower_generic_args_of_path(span, item_def_id, parent_args, item_segment, None); self.lower_generic_args_of_path(span, item_def_id, parent_args, item_segment, None);
if let Some(c) = item_segment.args().constraints.first() { if let Some(c) = item_segment.args().constraints.first() {
@ -1118,7 +1156,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// NOTE: When this function starts resolving `Trait::AssocTy` successfully // NOTE: When this function starts resolving `Trait::AssocTy` successfully
// it should also start reporting the `BARE_TRAIT_OBJECTS` lint. // it should also start reporting the `BARE_TRAIT_OBJECTS` lint.
#[instrument(level = "debug", skip_all, ret)] #[instrument(level = "debug", skip_all, ret)]
pub fn lower_assoc_path( pub fn lower_assoc_path_ty(
&self, &self,
hir_ref_id: HirId, hir_ref_id: HirId,
span: Span, span: Span,
@ -1127,6 +1165,72 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
assoc_segment: &'tcx hir::PathSegment<'tcx>, assoc_segment: &'tcx hir::PathSegment<'tcx>,
permit_variants: bool, permit_variants: bool,
) -> Result<(Ty<'tcx>, DefKind, DefId), ErrorGuaranteed> { ) -> Result<(Ty<'tcx>, DefKind, DefId), ErrorGuaranteed> {
let tcx = self.tcx();
match self.lower_assoc_path_shared(
hir_ref_id,
span,
qself_ty,
qself,
assoc_segment,
LowerAssocMode::Type { permit_variants },
)? {
LoweredAssoc::Term(def_id, args) => {
let alias_ty = ty::AliasTy::new_from_args(tcx, def_id, args);
let ty = Ty::new_alias(tcx, alias_ty.kind(tcx), alias_ty);
Ok((ty, tcx.def_kind(def_id), def_id))
}
LoweredAssoc::Variant { adt, variant_did } => Ok((adt, DefKind::Variant, variant_did)),
}
}
#[instrument(level = "debug", skip_all, ret)]
fn lower_assoc_path_const(
&self,
hir_ref_id: HirId,
span: Span,
qself_ty: Ty<'tcx>,
qself: &'tcx hir::Ty<'tcx>,
assoc_segment: &'tcx hir::PathSegment<'tcx>,
) -> Result<Const<'tcx>, ErrorGuaranteed> {
let tcx = self.tcx();
let (def_id, args) = match self.lower_assoc_path_shared(
hir_ref_id,
span,
qself_ty,
qself,
assoc_segment,
LowerAssocMode::Const,
)? {
LoweredAssoc::Term(def_id, args) => {
if !tcx.associated_item(def_id).is_type_const_capable(tcx) {
let mut err = tcx.dcx().struct_span_err(
span,
"use of trait associated const without `#[type_const]`",
);
err.note("the declaration in the trait must be marked with `#[type_const]`");
return Err(err.emit());
}
(def_id, args)
}
// FIXME(mgca): implement support for this once ready to support all adt ctor expressions,
// not just const ctors
LoweredAssoc::Variant { .. } => {
span_bug!(span, "unexpected variant res for type associated const path")
}
};
Ok(Const::new_unevaluated(tcx, ty::UnevaluatedConst::new(def_id, args)))
}
#[instrument(level = "debug", skip_all, ret)]
fn lower_assoc_path_shared(
&self,
hir_ref_id: HirId,
span: Span,
qself_ty: Ty<'tcx>,
qself: &'tcx hir::Ty<'tcx>,
assoc_segment: &'tcx hir::PathSegment<'tcx>,
mode: LowerAssocMode,
) -> Result<LoweredAssoc<'tcx>, ErrorGuaranteed> {
debug!(%qself_ty, ?assoc_segment.ident); debug!(%qself_ty, ?assoc_segment.ident);
let tcx = self.tcx(); let tcx = self.tcx();
@ -1141,13 +1245,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.iter() .iter()
.find(|vd| tcx.hygienic_eq(assoc_ident, vd.ident(tcx), adt_def.did())); .find(|vd| tcx.hygienic_eq(assoc_ident, vd.ident(tcx), adt_def.did()));
if let Some(variant_def) = variant_def { if let Some(variant_def) = variant_def {
if permit_variants { if mode.permit_variants() {
tcx.check_stability(variant_def.def_id, Some(hir_ref_id), span, None); tcx.check_stability(variant_def.def_id, Some(hir_ref_id), span, None);
let _ = self.prohibit_generic_args( let _ = self.prohibit_generic_args(
slice::from_ref(assoc_segment).iter(), slice::from_ref(assoc_segment).iter(),
GenericsArgsErrExtend::EnumVariant { qself, assoc_segment, adt_def }, GenericsArgsErrExtend::EnumVariant { qself, assoc_segment, adt_def },
); );
return Ok((qself_ty, DefKind::Variant, variant_def.def_id)); return Ok(LoweredAssoc::Variant {
adt: qself_ty,
variant_did: variant_def.def_id,
});
} else { } else {
variant_resolution = Some(variant_def.def_id); variant_resolution = Some(variant_def.def_id);
} }
@ -1155,15 +1262,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
} }
// FIXME(inherent_associated_types, #106719): Support self types other than ADTs. // FIXME(inherent_associated_types, #106719): Support self types other than ADTs.
if let Some((ty, did)) = self.probe_inherent_assoc_ty( if let Some((did, args)) = self.probe_inherent_assoc_shared(
assoc_ident,
assoc_segment, assoc_segment,
adt_def.did(), adt_def.did(),
qself_ty, qself_ty,
hir_ref_id, hir_ref_id,
span, span,
mode.kind(),
)? { )? {
return Ok((ty, DefKind::AssocTy, did)); return Ok(LoweredAssoc::Term(did, args));
} }
} }
@ -1192,7 +1299,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
) )
}, },
AssocItemQSelf::SelfTyAlias, AssocItemQSelf::SelfTyAlias,
ty::AssocKind::Type, mode.kind(),
assoc_ident, assoc_ident,
span, span,
None, None,
@ -1204,14 +1311,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
) => self.probe_single_ty_param_bound_for_assoc_item( ) => self.probe_single_ty_param_bound_for_assoc_item(
param_did.expect_local(), param_did.expect_local(),
qself.span, qself.span,
ty::AssocKind::Type, mode.kind(),
assoc_ident, assoc_ident,
span, span,
)?, )?,
_ => { _ => {
let kind_str = assoc_kind_str(mode.kind());
let reported = if variant_resolution.is_some() { let reported = if variant_resolution.is_some() {
// Variant in type position // Variant in type position
let msg = format!("expected type, found variant `{assoc_ident}`"); let msg = format!("expected {kind_str}, found variant `{assoc_ident}`");
self.dcx().span_err(span, msg) self.dcx().span_err(span, msg)
} else if qself_ty.is_enum() { } else if qself_ty.is_enum() {
let mut err = self.dcx().create_err(NoVariantNamed { let mut err = self.dcx().create_err(NoVariantNamed {
@ -1310,11 +1418,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
self.probe_traits_that_match_assoc_ty(qself_ty, assoc_ident); self.probe_traits_that_match_assoc_ty(qself_ty, assoc_ident);
// Don't print `ty::Error` to the user. // Don't print `ty::Error` to the user.
self.report_ambiguous_assoc_ty( self.report_ambiguous_assoc(
span, span,
&[qself_ty.to_string()], &[qself_ty.to_string()],
&traits, &traits,
assoc_ident.name, assoc_ident.name,
mode.kind(),
) )
}; };
return Err(reported); return Err(reported);
@ -1322,10 +1431,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}; };
let trait_did = bound.def_id(); let trait_did = bound.def_id();
let assoc_ty = self let assoc_item = self
.probe_assoc_item(assoc_ident, ty::AssocKind::Type, hir_ref_id, span, trait_did) .probe_assoc_item(assoc_ident, mode.kind(), hir_ref_id, span, trait_did)
.expect("failed to find associated type"); .expect("failed to find associated item");
let ty = self.lower_assoc_ty(span, assoc_ty.def_id, assoc_segment, bound); let (def_id, args) =
self.lower_assoc_shared(span, assoc_item.def_id, assoc_segment, bound, mode.kind())?;
let result = LoweredAssoc::Term(def_id, args);
if let Some(variant_def_id) = variant_resolution { if let Some(variant_def_id) = variant_resolution {
tcx.node_span_lint(AMBIGUOUS_ASSOCIATED_ITEMS, hir_ref_id, span, |lint| { tcx.node_span_lint(AMBIGUOUS_ASSOCIATED_ITEMS, hir_ref_id, span, |lint| {
@ -1341,7 +1452,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}; };
could_refer_to(DefKind::Variant, variant_def_id, ""); could_refer_to(DefKind::Variant, variant_def_id, "");
could_refer_to(DefKind::AssocTy, assoc_ty.def_id, " also"); could_refer_to(mode.def_kind(), assoc_item.def_id, " also");
lint.span_suggestion( lint.span_suggestion(
span, span,
@ -1351,36 +1462,51 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
); );
}); });
} }
Ok((ty, DefKind::AssocTy, assoc_ty.def_id)) Ok(result)
} }
fn probe_inherent_assoc_ty( fn probe_inherent_assoc_shared(
&self, &self,
name: Ident,
segment: &hir::PathSegment<'tcx>, segment: &hir::PathSegment<'tcx>,
adt_did: DefId, adt_did: DefId,
self_ty: Ty<'tcx>, self_ty: Ty<'tcx>,
block: HirId, block: HirId,
span: Span, span: Span,
) -> Result<Option<(Ty<'tcx>, DefId)>, ErrorGuaranteed> { kind: ty::AssocKind,
) -> Result<Option<(DefId, GenericArgsRef<'tcx>)>, ErrorGuaranteed> {
let tcx = self.tcx(); let tcx = self.tcx();
if !tcx.features().inherent_associated_types() {
match kind {
// Don't attempt to look up inherent associated types when the feature is not enabled. // Don't attempt to look up inherent associated types when the feature is not enabled.
// Theoretically it'd be fine to do so since we feature-gate their definition site. // Theoretically it'd be fine to do so since we feature-gate their definition site.
// However, due to current limitations of the implementation (caused by us performing // However, due to current limitations of the implementation (caused by us performing
// selection during HIR ty lowering instead of in the trait solver), IATs can lead to cycle // selection during HIR ty lowering instead of in the trait solver), IATs can lead to cycle
// errors (#108491) which mask the feature-gate error, needlessly confusing users // errors (#108491) which mask the feature-gate error, needlessly confusing users
// who use IATs by accident (#113265). // who use IATs by accident (#113265).
if !tcx.features().inherent_associated_types() { ty::AssocKind::Type => return Ok(None),
return Ok(None); ty::AssocKind::Const => {
// We also gate the mgca codepath for type-level uses of inherent consts
// with the inherent_associated_types feature gate since it relies on the
// same machinery and has similar rough edges.
return Err(feature_err(
&tcx.sess,
sym::inherent_associated_types,
span,
"inherent associated types are unstable",
)
.emit());
}
ty::AssocKind::Fn => unreachable!(),
}
} }
let name = segment.ident;
let candidates: Vec<_> = tcx let candidates: Vec<_> = tcx
.inherent_impls(adt_did) .inherent_impls(adt_did)
.iter() .iter()
.filter_map(|&impl_| { .filter_map(|&impl_| {
let (item, scope) = let (item, scope) = self.probe_assoc_item_unchecked(name, kind, block, impl_)?;
self.probe_assoc_item_unchecked(name, ty::AssocKind::Type, block, impl_)?;
Some((impl_, (item.def_id, scope))) Some((impl_, (item.def_id, scope)))
}) })
.collect(); .collect();
@ -1395,13 +1521,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// In contexts that have no inference context, just make a new one. // In contexts that have no inference context, just make a new one.
// We do need a local variable to store it, though. // We do need a local variable to store it, though.
let infcx_;
let infcx = match self.infcx() { let infcx = match self.infcx() {
Some(infcx) => infcx, Some(infcx) => infcx,
None => { None => {
assert!(!self_ty.has_infer()); assert!(!self_ty.has_infer());
infcx_ = tcx.infer_ctxt().ignoring_regions().build(TypingMode::non_body_analysis()); &tcx.infer_ctxt().ignoring_regions().build(TypingMode::non_body_analysis())
&infcx_
} }
}; };
@ -1420,8 +1544,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
&mut universes, &mut universes,
self_ty, self_ty,
|self_ty| { |self_ty| {
self.select_inherent_assoc_type_candidates( self.select_inherent_assoc_candidates(
infcx, name, span, self_ty, param_env, candidates, infcx, name, span, self_ty, param_env, candidates, kind,
) )
}, },
)?; )?;
@ -1438,13 +1562,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.chain(args.into_iter().skip(parent_args.len())), .chain(args.into_iter().skip(parent_args.len())),
); );
let ty = Ok(Some((assoc_item, args)))
Ty::new_alias(tcx, ty::Inherent, ty::AliasTy::new_from_args(tcx, assoc_item, args));
Ok(Some((ty, assoc_item)))
} }
fn select_inherent_assoc_type_candidates( fn select_inherent_assoc_candidates(
&self, &self,
infcx: &InferCtxt<'tcx>, infcx: &InferCtxt<'tcx>,
name: Ident, name: Ident,
@ -1452,6 +1573,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
self_ty: Ty<'tcx>, self_ty: Ty<'tcx>,
param_env: ParamEnv<'tcx>, param_env: ParamEnv<'tcx>,
candidates: Vec<(DefId, (DefId, DefId))>, candidates: Vec<(DefId, (DefId, DefId))>,
kind: ty::AssocKind,
) -> Result<(DefId, (DefId, DefId)), ErrorGuaranteed> { ) -> Result<(DefId, (DefId, DefId)), ErrorGuaranteed> {
let tcx = self.tcx(); let tcx = self.tcx();
let mut fulfillment_errors = Vec::new(); let mut fulfillment_errors = Vec::new();
@ -1496,17 +1618,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.collect(); .collect();
match &applicable_candidates[..] { match &applicable_candidates[..] {
&[] => Err(self.complain_about_inherent_assoc_ty_not_found( &[] => Err(self.complain_about_inherent_assoc_not_found(
name, name,
self_ty, self_ty,
candidates, candidates,
fulfillment_errors, fulfillment_errors,
span, span,
kind,
)), )),
&[applicable_candidate] => Ok(applicable_candidate), &[applicable_candidate] => Ok(applicable_candidate),
&[_, ..] => Err(self.complain_about_ambiguous_inherent_assoc_ty( &[_, ..] => Err(self.complain_about_ambiguous_inherent_assoc(
name, name,
applicable_candidates.into_iter().map(|(_, (candidate, _))| candidate).collect(), applicable_candidates.into_iter().map(|(_, (candidate, _))| candidate).collect(),
span, span,
@ -1638,7 +1761,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
/// Lower a qualified path to a type. /// Lower a qualified path to a type.
#[instrument(level = "debug", skip_all)] #[instrument(level = "debug", skip_all)]
fn lower_qpath( fn lower_qpath_ty(
&self, &self,
span: Span, span: Span,
opt_self_ty: Option<Ty<'tcx>>, opt_self_ty: Option<Ty<'tcx>>,
@ -1646,13 +1769,64 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
trait_segment: &hir::PathSegment<'tcx>, trait_segment: &hir::PathSegment<'tcx>,
item_segment: &hir::PathSegment<'tcx>, item_segment: &hir::PathSegment<'tcx>,
) -> Ty<'tcx> { ) -> Ty<'tcx> {
match self.lower_qpath_shared(
span,
opt_self_ty,
item_def_id,
trait_segment,
item_segment,
ty::AssocKind::Type,
) {
Ok((item_def_id, item_args)) => {
Ty::new_projection_from_args(self.tcx(), item_def_id, item_args)
}
Err(guar) => Ty::new_error(self.tcx(), guar),
}
}
/// Lower a qualified path to a const.
#[instrument(level = "debug", skip_all)]
fn lower_qpath_const(
&self,
span: Span,
opt_self_ty: Option<Ty<'tcx>>,
item_def_id: DefId,
trait_segment: &hir::PathSegment<'tcx>,
item_segment: &hir::PathSegment<'tcx>,
) -> Const<'tcx> {
match self.lower_qpath_shared(
span,
opt_self_ty,
item_def_id,
trait_segment,
item_segment,
ty::AssocKind::Const,
) {
Ok((item_def_id, item_args)) => {
let uv = ty::UnevaluatedConst::new(item_def_id, item_args);
Const::new_unevaluated(self.tcx(), uv)
}
Err(guar) => Const::new_error(self.tcx(), guar),
}
}
#[instrument(level = "debug", skip_all)]
fn lower_qpath_shared(
&self,
span: Span,
opt_self_ty: Option<Ty<'tcx>>,
item_def_id: DefId,
trait_segment: &hir::PathSegment<'tcx>,
item_segment: &hir::PathSegment<'tcx>,
kind: ty::AssocKind,
) -> Result<(DefId, GenericArgsRef<'tcx>), ErrorGuaranteed> {
let tcx = self.tcx(); let tcx = self.tcx();
let trait_def_id = tcx.parent(item_def_id); let trait_def_id = tcx.parent(item_def_id);
debug!(?trait_def_id); debug!(?trait_def_id);
let Some(self_ty) = opt_self_ty else { let Some(self_ty) = opt_self_ty else {
return self.error_missing_qpath_self_ty(trait_def_id, span, item_segment); return Err(self.error_missing_qpath_self_ty(trait_def_id, span, item_segment, kind));
}; };
debug!(?self_ty); debug!(?self_ty);
@ -1663,7 +1837,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let item_args = let item_args =
self.lower_generic_args_of_assoc_item(span, item_def_id, item_segment, trait_ref.args); self.lower_generic_args_of_assoc_item(span, item_def_id, item_segment, trait_ref.args);
Ty::new_projection_from_args(tcx, item_def_id, item_args) Ok((item_def_id, item_args))
} }
fn error_missing_qpath_self_ty( fn error_missing_qpath_self_ty(
@ -1671,7 +1845,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
trait_def_id: DefId, trait_def_id: DefId,
span: Span, span: Span,
item_segment: &hir::PathSegment<'tcx>, item_segment: &hir::PathSegment<'tcx>,
) -> Ty<'tcx> { kind: ty::AssocKind,
) -> ErrorGuaranteed {
let tcx = self.tcx(); let tcx = self.tcx();
let path_str = tcx.def_path_str(trait_def_id); let path_str = tcx.def_path_str(trait_def_id);
@ -1707,9 +1882,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// FIXME: also look at `tcx.generics_of(self.item_def_id()).params` any that // FIXME: also look at `tcx.generics_of(self.item_def_id()).params` any that
// references the trait. Relevant for the first case in // references the trait. Relevant for the first case in
// `src/test/ui/associated-types/associated-types-in-ambiguous-context.rs` // `src/test/ui/associated-types/associated-types-in-ambiguous-context.rs`
let reported = self.report_ambiguous_assoc(span, &type_names, &[path_str], item_segment.ident.name, kind)
self.report_ambiguous_assoc_ty(span, &type_names, &[path_str], item_segment.ident.name);
Ty::new_error(tcx, reported)
} }
pub fn prohibit_generic_args<'a>( pub fn prohibit_generic_args<'a>(
@ -2013,7 +2186,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
path.segments[..path.segments.len() - 2].iter(), path.segments[..path.segments.len() - 2].iter(),
GenericsArgsErrExtend::None, GenericsArgsErrExtend::None,
); );
self.lower_qpath( self.lower_qpath_ty(
span, span,
opt_self_ty, opt_self_ty,
def_id, def_id,
@ -2167,11 +2340,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself)); let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself));
self.lower_const_path_resolved(opt_self_ty, path, hir_id) self.lower_const_path_resolved(opt_self_ty, path, hir_id)
} }
hir::ConstArgKind::Path(qpath) => ty::Const::new_error_with_message( hir::ConstArgKind::Path(hir::QPath::TypeRelative(qself, segment)) => {
debug!(?qself, ?segment);
let ty = self.lower_ty(qself);
self.lower_assoc_path_const(hir_id, const_arg.span(), ty, qself, segment)
.unwrap_or_else(|guar| Const::new_error(tcx, guar))
}
hir::ConstArgKind::Path(qpath @ hir::QPath::LangItem(..)) => {
ty::Const::new_error_with_message(
tcx, tcx,
qpath.span(), qpath.span(),
format!("Const::lower_const_arg: invalid qpath {qpath:?}"), format!("Const::lower_const_arg: invalid qpath {qpath:?}"),
), )
}
hir::ConstArgKind::Anon(anon) => self.lower_anon_const(anon), hir::ConstArgKind::Anon(anon) => self.lower_anon_const(anon),
hir::ConstArgKind::Infer(span, ()) => self.ct_infer(None, span), hir::ConstArgKind::Infer(span, ()) => self.ct_infer(None, span),
} }
@ -2207,6 +2388,20 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
); );
ty::Const::new_unevaluated(tcx, ty::UnevaluatedConst::new(did, args)) ty::Const::new_unevaluated(tcx, ty::UnevaluatedConst::new(did, args))
} }
Res::Def(DefKind::AssocConst, did) => {
debug_assert!(path.segments.len() >= 2);
let _ = self.prohibit_generic_args(
path.segments[..path.segments.len() - 2].iter(),
GenericsArgsErrExtend::None,
);
self.lower_qpath_const(
span,
opt_self_ty,
did,
&path.segments[path.segments.len() - 2],
path.segments.last().unwrap(),
)
}
Res::Def(DefKind::Static { .. }, _) => { Res::Def(DefKind::Static { .. }, _) => {
span_bug!(span, "use of bare `static` ConstArgKind::Path's not yet supported") span_bug!(span, "use of bare `static` ConstArgKind::Path's not yet supported")
} }
@ -2223,7 +2418,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// Exhaustive match to be clear about what exactly we're considering to be // Exhaustive match to be clear about what exactly we're considering to be
// an invalid Res for a const path. // an invalid Res for a const path.
Res::Def( res @ (Res::Def(
DefKind::Mod DefKind::Mod
| DefKind::Enum | DefKind::Enum
| DefKind::Variant | DefKind::Variant
@ -2237,7 +2432,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
| DefKind::Union | DefKind::Union
| DefKind::Trait | DefKind::Trait
| DefKind::ForeignTy | DefKind::ForeignTy
| DefKind::AssocConst
| DefKind::TyParam | DefKind::TyParam
| DefKind::Macro(_) | DefKind::Macro(_)
| DefKind::LifetimeParam | DefKind::LifetimeParam
@ -2260,12 +2454,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
| Res::Local(_) | Res::Local(_)
| Res::ToolMod | Res::ToolMod
| Res::NonMacroAttr(_) | Res::NonMacroAttr(_)
| Res::Err => Const::new_error_with_message(tcx, span, "invalid Res for const path"), | Res::Err) => Const::new_error_with_message(
tcx,
span,
format!("invalid Res {res:?} for const path"),
),
} }
} }
/// Literals and const generic parameters are eagerly converted to a constant, everything else /// Literals are eagerly converted to a constant, everything else becomes `Unevaluated`.
/// becomes `Unevaluated`.
#[instrument(skip(self), level = "debug")] #[instrument(skip(self), level = "debug")]
fn lower_anon_const(&self, anon: &AnonConst) -> Const<'tcx> { fn lower_anon_const(&self, anon: &AnonConst) -> Const<'tcx> {
let tcx = self.tcx(); let tcx = self.tcx();
@ -2464,7 +2661,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
hir::TyKind::Path(hir::QPath::TypeRelative(qself, segment)) => { hir::TyKind::Path(hir::QPath::TypeRelative(qself, segment)) => {
debug!(?qself, ?segment); debug!(?qself, ?segment);
let ty = self.lower_ty(qself); let ty = self.lower_ty(qself);
self.lower_assoc_path(hir_ty.hir_id, hir_ty.span, ty, qself, segment, false) self.lower_assoc_path_ty(hir_ty.hir_id, hir_ty.span, ty, qself, segment, false)
.map(|(ty, _, _)| ty) .map(|(ty, _, _)| ty)
.unwrap_or_else(|guar| Ty::new_error(tcx, guar)) .unwrap_or_else(|guar| Ty::new_error(tcx, guar))
} }

View file

@ -2174,7 +2174,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let result = self let result = self
.lowerer() .lowerer()
.lower_assoc_path(hir_id, path_span, ty.raw, qself, segment, true); .lower_assoc_path_ty(hir_id, path_span, ty.raw, qself, segment, true);
let ty = result let ty = result
.map(|(ty, _, _)| ty) .map(|(ty, _, _)| ty)
.unwrap_or_else(|guar| Ty::new_error(self.tcx(), guar)); .unwrap_or_else(|guar| Ty::new_error(self.tcx(), guar));

View file

@ -17,7 +17,7 @@ use rustc_infer::infer;
use rustc_infer::traits::Obligation; use rustc_infer::traits::Obligation;
use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt};
use rustc_session::Session; use rustc_session::Session;
use rustc_span::{self, DUMMY_SP, Ident, Span, sym}; use rustc_span::{self, DUMMY_SP, ErrorGuaranteed, Ident, Span, sym};
use rustc_trait_selection::error_reporting::TypeErrCtxt; use rustc_trait_selection::error_reporting::TypeErrCtxt;
use rustc_trait_selection::error_reporting::infer::sub_relations::SubRelations; use rustc_trait_selection::error_reporting::infer::sub_relations::SubRelations;
use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt}; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt};
@ -308,15 +308,17 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> {
)) ))
} }
fn lower_assoc_ty( fn lower_assoc_shared(
&self, &self,
span: Span, span: Span,
item_def_id: DefId, item_def_id: DefId,
item_segment: &hir::PathSegment<'tcx>, item_segment: &rustc_hir::PathSegment<'tcx>,
poly_trait_ref: ty::PolyTraitRef<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>,
) -> Ty<'tcx> { _kind: ty::AssocKind,
) -> Result<(DefId, ty::GenericArgsRef<'tcx>), ErrorGuaranteed> {
let trait_ref = self.instantiate_binder_with_fresh_vars( let trait_ref = self.instantiate_binder_with_fresh_vars(
span, span,
// FIXME(mgca): this should be assoc const if that is the `kind`
infer::BoundRegionConversionTime::AssocTypeProjection(item_def_id), infer::BoundRegionConversionTime::AssocTypeProjection(item_def_id),
poly_trait_ref, poly_trait_ref,
); );
@ -328,7 +330,7 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> {
trait_ref.args, trait_ref.args,
); );
Ty::new_projection_from_args(self.tcx(), item_def_id, item_args) Ok((item_def_id, item_args))
} }
fn probe_adt(&self, span: Span, ty: Ty<'tcx>) -> Option<ty::AdtDef<'tcx>> { fn probe_adt(&self, span: Span, ty: Ty<'tcx>) -> Option<ty::AdtDef<'tcx>> {

View file

@ -3,7 +3,7 @@ use rustc_hir as hir;
use rustc_hir::def::{DefKind, Namespace}; use rustc_hir::def::{DefKind, Namespace};
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_macros::{Decodable, Encodable, HashStable};
use rustc_span::{Ident, Symbol}; use rustc_span::{Ident, Symbol, sym};
use super::{TyCtxt, Visibility}; use super::{TyCtxt, Visibility};
use crate::ty; use crate::ty;
@ -108,6 +108,24 @@ impl AssocItem {
pub fn is_impl_trait_in_trait(&self) -> bool { pub fn is_impl_trait_in_trait(&self) -> bool {
self.opt_rpitit_info.is_some() self.opt_rpitit_info.is_some()
} }
/// Returns true if:
/// - This trait associated item has the `#[type_const]` attribute,
/// - If it is in a trait impl, the item from the original trait has this attribute, or
/// - It is an inherent assoc const.
pub fn is_type_const_capable(&self, tcx: TyCtxt<'_>) -> bool {
if self.kind != ty::AssocKind::Const {
return false;
}
let def_id = match (self.container, self.trait_item_def_id) {
(AssocItemContainer::Trait, _) => self.def_id,
(AssocItemContainer::Impl, Some(trait_item_did)) => trait_item_did,
// Inherent impl but this attr is only applied to trait assoc items.
(AssocItemContainer::Impl, None) => return true,
};
tcx.has_attr(def_id, sym::type_const)
}
} }
#[derive(Copy, Clone, PartialEq, Debug, HashStable, Eq, Hash, Encodable, Decodable)] #[derive(Copy, Clone, PartialEq, Debug, HashStable, Eq, Hash, Encodable, Decodable)]

View file

@ -14,6 +14,7 @@ use rustc_attr_parsing::{AttributeKind, ReprAttr, find_attr};
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{Applicability, DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey}; use rustc_errors::{Applicability, DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey};
use rustc_feature::{AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute}; use rustc_feature::{AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute};
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalModDefId; use rustc_hir::def_id::LocalModDefId;
use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{ use rustc_hir::{
@ -257,6 +258,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
[sym::coroutine, ..] => { [sym::coroutine, ..] => {
self.check_coroutine(attr, target); self.check_coroutine(attr, target);
} }
[sym::type_const, ..] => {
self.check_type_const(hir_id,attr, target);
}
[sym::linkage, ..] => self.check_linkage(attr, span, target), [sym::linkage, ..] => self.check_linkage(attr, span, target),
[sym::rustc_pub_transparent, ..] => self.check_rustc_pub_transparent(attr.span(), span, attrs), [sym::rustc_pub_transparent, ..] => self.check_rustc_pub_transparent(attr.span(), span, attrs),
[ [
@ -2519,6 +2523,23 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
} }
} }
fn check_type_const(&self, hir_id: HirId, attr: &Attribute, target: Target) {
let tcx = self.tcx;
if target == Target::AssocConst
&& let parent = tcx.parent(hir_id.expect_owner().to_def_id())
&& self.tcx.def_kind(parent) == DefKind::Trait
{
return;
} else {
self.dcx()
.struct_span_err(
attr.span(),
"`#[type_const]` must only be applied to trait associated constants",
)
.emit();
}
}
fn check_linkage(&self, attr: &Attribute, span: Span, target: Target) { fn check_linkage(&self, attr: &Attribute, span: Span, target: Target) {
match target { match target {
Target::Fn Target::Fn

View file

@ -1202,7 +1202,7 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
if let TyKind::Path(None, ref path) = ty.kind if let TyKind::Path(None, ref path) = ty.kind
// We cannot disambiguate multi-segment paths right now as that requires type // We cannot disambiguate multi-segment paths right now as that requires type
// checking. // checking.
&& path.is_potential_trivial_const_arg() && path.is_potential_trivial_const_arg(false)
{ {
let mut check_ns = |ns| { let mut check_ns = |ns| {
self.maybe_resolve_ident_in_lexical_scope(path.segments[0].ident, ns) self.maybe_resolve_ident_in_lexical_scope(path.segments[0].ident, ns)
@ -4630,11 +4630,12 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
constant, anon_const_kind constant, anon_const_kind
); );
self.resolve_anon_const_manual( let is_trivial_const_arg = constant
constant.value.is_potential_trivial_const_arg(), .value
anon_const_kind, .is_potential_trivial_const_arg(self.r.tcx.features().min_generic_const_args());
|this| this.resolve_expr(&constant.value, None), self.resolve_anon_const_manual(is_trivial_const_arg, anon_const_kind, |this| {
) this.resolve_expr(&constant.value, None)
})
} }
/// There are a few places that we need to resolve an anon const but we did not parse an /// There are a few places that we need to resolve an anon const but we did not parse an
@ -4794,8 +4795,11 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
// Constant arguments need to be treated as AnonConst since // Constant arguments need to be treated as AnonConst since
// that is how they will be later lowered to HIR. // that is how they will be later lowered to HIR.
if const_args.contains(&idx) { if const_args.contains(&idx) {
let is_trivial_const_arg = argument.is_potential_trivial_const_arg(
self.r.tcx.features().min_generic_const_args(),
);
self.resolve_anon_const_manual( self.resolve_anon_const_manual(
argument.is_potential_trivial_const_arg(), is_trivial_const_arg,
AnonConstKind::ConstArg(IsRepeatExpr::No), AnonConstKind::ConstArg(IsRepeatExpr::No),
|this| this.resolve_expr(argument, None), |this| this.resolve_expr(argument, None),
); );

View file

@ -2084,6 +2084,7 @@ symbols! {
type_ascribe, type_ascribe,
type_ascription, type_ascription,
type_changing_struct_update, type_changing_struct_update,
type_const,
type_id, type_id,
type_ir_inherent, type_ir_inherent,
type_length_limit, type_length_limit,

View file

@ -3250,7 +3250,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
obligation: &PredicateObligation<'tcx>, obligation: &PredicateObligation<'tcx>,
span: Span, span: Span,
) -> Result<Diag<'a>, ErrorGuaranteed> { ) -> Result<Diag<'a>, ErrorGuaranteed> {
if !self.tcx.features().generic_const_exprs() { if !self.tcx.features().generic_const_exprs()
&& !self.tcx.features().min_generic_const_args()
{
let guar = self let guar = self
.dcx() .dcx()
.struct_span_err(span, "constant expression depends on a generic parameter") .struct_span_err(span, "constant expression depends on a generic parameter")

View file

@ -85,6 +85,12 @@ pub fn is_const_evaluatable<'tcx>(
} }
_ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"), _ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"),
} }
} else if tcx.features().min_generic_const_args() {
// This is a sanity check to make sure that non-generics consts are checked to
// be evaluatable in case they aren't cchecked elsewhere. This will NOT error
// if the const uses generics, as desired.
crate::traits::evaluate_const(infcx, unexpanded_ct, param_env);
Ok(())
} else { } else {
let uv = match unexpanded_ct.kind() { let uv = match unexpanded_ct.kind() {
ty::ConstKind::Unevaluated(uv) => uv, ty::ConstKind::Unevaluated(uv) => uv,

View file

@ -410,7 +410,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let predicate = obligation.predicate.skip_binder(); let predicate = obligation.predicate.skip_binder();
let mut assume = predicate.trait_ref.args.const_at(2); let mut assume = predicate.trait_ref.args.const_at(2);
// FIXME(min_generic_const_exprs): We should shallowly normalize this. // FIXME(mgca): We should shallowly normalize this.
if self.tcx().features().generic_const_exprs() { if self.tcx().features().generic_const_exprs() {
assume = crate::traits::evaluate_const(self.infcx, assume, obligation.param_env) assume = crate::traits::evaluate_const(self.infcx, assume, obligation.param_env)
} }

View file

@ -2,7 +2,7 @@ error[E0220]: associated type `Cursor` not found for `Lexer<T>` in the current s
--> $DIR/issue-109299-1.rs:10:40 --> $DIR/issue-109299-1.rs:10:40
| |
LL | struct Lexer<T>(T); LL | struct Lexer<T>(T);
| --------------- associated item `Cursor` not found for this struct | --------------- associated type `Cursor` not found for this struct
... ...
LL | type X = impl for<T> Fn() -> Lexer<T>::Cursor; LL | type X = impl for<T> Fn() -> Lexer<T>::Cursor;
| ^^^^^^ associated item not found in `Lexer<T>` | ^^^^^^ associated item not found in `Lexer<T>`
@ -14,7 +14,7 @@ error[E0220]: associated type `Cursor` not found for `Lexer<T>` in the current s
--> $DIR/issue-109299-1.rs:10:40 --> $DIR/issue-109299-1.rs:10:40
| |
LL | struct Lexer<T>(T); LL | struct Lexer<T>(T);
| --------------- associated item `Cursor` not found for this struct | --------------- associated type `Cursor` not found for this struct
... ...
LL | type X = impl for<T> Fn() -> Lexer<T>::Cursor; LL | type X = impl for<T> Fn() -> Lexer<T>::Cursor;
| ^^^^^^ associated item not found in `Lexer<T>` | ^^^^^^ associated item not found in `Lexer<T>`

View file

@ -2,7 +2,7 @@ error[E0220]: associated type `Pr` not found for `S<bool>` in the current scope
--> $DIR/not-found-self-type-differs-shadowing-trait-item.rs:28:23 --> $DIR/not-found-self-type-differs-shadowing-trait-item.rs:28:23
| |
LL | struct S<T>(T); LL | struct S<T>(T);
| ----------- associated item `Pr` not found for this struct | ----------- associated type `Pr` not found for this struct
... ...
LL | let _: S::<bool>::Pr = (); LL | let _: S::<bool>::Pr = ();
| ^^ associated item not found in `S<bool>` | ^^ associated item not found in `S<bool>`

View file

@ -2,7 +2,7 @@ error[E0220]: associated type `Proj` not found for `Family<Option<()>>` in the c
--> $DIR/not-found-self-type-differs.rs:15:32 --> $DIR/not-found-self-type-differs.rs:15:32
| |
LL | struct Family<T>(T); LL | struct Family<T>(T);
| ---------------- associated item `Proj` not found for this struct | ---------------- associated type `Proj` not found for this struct
... ...
LL | let _: Family<Option<()>>::Proj; LL | let _: Family<Option<()>>::Proj;
| ^^^^ associated item not found in `Family<Option<()>>` | ^^^^ associated item not found in `Family<Option<()>>`
@ -15,7 +15,7 @@ error[E0220]: associated type `Proj` not found for `Family<PathBuf>` in the curr
--> $DIR/not-found-self-type-differs.rs:16:40 --> $DIR/not-found-self-type-differs.rs:16:40
| |
LL | struct Family<T>(T); LL | struct Family<T>(T);
| ---------------- associated item `Proj` not found for this struct | ---------------- associated type `Proj` not found for this struct
... ...
LL | let _: Family<std::path::PathBuf>::Proj = (); LL | let _: Family<std::path::PathBuf>::Proj = ();
| ^^^^ associated item not found in `Family<PathBuf>` | ^^^^ associated item not found in `Family<PathBuf>`

View file

@ -2,7 +2,7 @@ error: the associated type `Yield` exists for `Container<[u8]>`, but its trait b
--> $DIR/not-found-unsatisfied-bounds-0.rs:19:29 --> $DIR/not-found-unsatisfied-bounds-0.rs:19:29
| |
LL | struct Container<T: ?Sized>(T); LL | struct Container<T: ?Sized>(T);
| --------------------------- associated item `Yield` not found for this struct | --------------------------- associated type `Yield` not found for this struct
... ...
LL | let _: Container<[u8]>::Yield = 1; LL | let _: Container<[u8]>::Yield = 1;
| ^^^^^ associated type cannot be referenced on `Container<[u8]>` due to unsatisfied trait bounds | ^^^^^ associated type cannot be referenced on `Container<[u8]>` due to unsatisfied trait bounds
@ -14,7 +14,7 @@ error: the associated type `Combination` exists for `Duple<String, Rc<str>>`, bu
--> $DIR/not-found-unsatisfied-bounds-0.rs:20:45 --> $DIR/not-found-unsatisfied-bounds-0.rs:20:45
| |
LL | struct Duple<T, U>(T, U); LL | struct Duple<T, U>(T, U);
| ------------------ associated item `Combination` not found for this struct | ------------------ associated type `Combination` not found for this struct
... ...
LL | let _: Duple<String, std::rc::Rc<str>>::Combination; LL | let _: Duple<String, std::rc::Rc<str>>::Combination;
| ^^^^^^^^^^^ associated type cannot be referenced on `Duple<String, Rc<str>>` due to unsatisfied trait bounds | ^^^^^^^^^^^ associated type cannot be referenced on `Duple<String, Rc<str>>` due to unsatisfied trait bounds

View file

@ -5,7 +5,7 @@ LL | let _: Container<T>::Proj = String::new();
| ^^^^ associated type cannot be referenced on `Container<T>` due to unsatisfied trait bounds | ^^^^ associated type cannot be referenced on `Container<T>` due to unsatisfied trait bounds
... ...
LL | struct Container<T>(T); LL | struct Container<T>(T);
| ------------------- associated item `Proj` not found for this struct | ------------------- associated type `Proj` not found for this struct
| |
= note: the following trait bounds were not satisfied: = note: the following trait bounds were not satisfied:
`T: Clone` `T: Clone`

View file

@ -2,7 +2,7 @@ error: the associated type `X` exists for `S<Featureless, Featureless>`, but its
--> $DIR/not-found-unsatisfied-bounds-in-multiple-impls.rs:19:43 --> $DIR/not-found-unsatisfied-bounds-in-multiple-impls.rs:19:43
| |
LL | struct S<A, B>(A, B); LL | struct S<A, B>(A, B);
| -------------- associated item `X` not found for this struct | -------------- associated type `X` not found for this struct
LL | struct Featureless; LL | struct Featureless;
| ------------------ doesn't satisfy `Featureless: One` or `Featureless: Two` | ------------------ doesn't satisfy `Featureless: One` or `Featureless: Two`
... ...

View file

@ -0,0 +1,15 @@
#![feature(min_generic_const_args)]
#![expect(incomplete_features)]
trait Tr {
const N: usize;
}
struct Blah<const N: usize>;
fn foo() -> Blah<{ Tr::N }> {
//~^ ERROR ambiguous associated constant
todo!()
}
fn main() {}

View file

@ -0,0 +1,15 @@
error[E0223]: ambiguous associated constant
--> $DIR/ambiguous-assoc-const.rs:10:20
|
LL | fn foo() -> Blah<{ Tr::N }> {
| ^^^^^
|
help: if there were a type named `Example` that implemented `Tr`, you could use the fully-qualified path
|
LL - fn foo() -> Blah<{ Tr::N }> {
LL + fn foo() -> Blah<{ <Example as Tr>::N }> {
|
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0223`.

View file

@ -0,0 +1,14 @@
#![feature(min_generic_const_args)]
#![allow(incomplete_features)]
pub trait Tr {
const SIZE: usize;
}
fn mk_array<T: Tr>(_x: T) -> [(); T::SIZE] {
//~^ ERROR type_const
[(); T::SIZE]
//~^ ERROR type_const
}
fn main() {}

View file

@ -0,0 +1,18 @@
error: use of trait associated const without `#[type_const]`
--> $DIR/assoc-const-without-type_const.rs:8:35
|
LL | fn mk_array<T: Tr>(_x: T) -> [(); T::SIZE] {
| ^^^^^^^
|
= note: the declaration in the trait must be marked with `#[type_const]`
error: use of trait associated const without `#[type_const]`
--> $DIR/assoc-const-without-type_const.rs:10:10
|
LL | [(); T::SIZE]
| ^^^^^^^
|
= note: the declaration in the trait must be marked with `#[type_const]`
error: aborting due to 2 previous errors

View file

@ -0,0 +1,15 @@
//@ check-pass
#![feature(min_generic_const_args)]
#![allow(incomplete_features)]
pub trait Tr<X> {
#[type_const]
const SIZE: usize;
}
fn mk_array<T: Tr<bool>>(_x: T) -> [(); <T as Tr<bool>>::SIZE] {
[(); T::SIZE]
}
fn main() {}

View file

@ -0,0 +1,17 @@
trait Tr {
#[type_const()]
//~^ ERROR malformed
//~| ERROR experimental
const N: usize;
}
struct S;
impl Tr for S {
#[type_const]
//~^ ERROR must only be applied to trait associated constants
//~| ERROR experimental
const N: usize = 0;
}
fn main() {}

View file

@ -0,0 +1,35 @@
error: malformed `type_const` attribute input
--> $DIR/bad-type_const-syntax.rs:2:5
|
LL | #[type_const()]
| ^^^^^^^^^^^^^^^ help: must be of the form: `#[type_const]`
error[E0658]: the `#[type_const]` attribute is an experimental feature
--> $DIR/bad-type_const-syntax.rs:2:5
|
LL | #[type_const()]
| ^^^^^^^^^^^^^^^
|
= note: see issue #132980 <https://github.com/rust-lang/rust/issues/132980> for more information
= help: add `#![feature(min_generic_const_args)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: the `#[type_const]` attribute is an experimental feature
--> $DIR/bad-type_const-syntax.rs:11:5
|
LL | #[type_const]
| ^^^^^^^^^^^^^
|
= note: see issue #132980 <https://github.com/rust-lang/rust/issues/132980> for more information
= help: add `#![feature(min_generic_const_args)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: `#[type_const]` must only be applied to trait associated constants
--> $DIR/bad-type_const-syntax.rs:11:5
|
LL | #[type_const]
| ^^^^^^^^^^^^^
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0658`.

View file

@ -0,0 +1,13 @@
#![feature(min_generic_const_args)]
#![allow(incomplete_features)]
struct S;
impl S {
const N: usize = 42;
}
fn main() {
let _x: [(); S::N] = todo!();
//~^ ERROR inherent associated types are unstable
}

View file

@ -0,0 +1,13 @@
error[E0658]: inherent associated types are unstable
--> $DIR/inherent-const-gating.rs:11:18
|
LL | let _x: [(); S::N] = todo!();
| ^^^^
|
= note: see issue #8995 <https://github.com/rust-lang/rust/issues/8995> for more information
= help: add `#![feature(inherent_associated_types)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0658`.

View file

@ -1,8 +1,10 @@
trait Trait { trait Trait {
#[type_const]
//~^ ERROR experimental
const ASSOC: usize; const ASSOC: usize;
} }
// FIXME(min_generic_const_args): implement support for this, behind the feature gate // FIXME(mgca): add suggestion for mgca to this error
fn foo<T: Trait>() -> [u8; <T as Trait>::ASSOC] { fn foo<T: Trait>() -> [u8; <T as Trait>::ASSOC] {
//~^ ERROR generic parameters may not be used in const operations //~^ ERROR generic parameters may not be used in const operations
loop {} loop {}

View file

@ -1,5 +1,5 @@
error: generic parameters may not be used in const operations error: generic parameters may not be used in const operations
--> $DIR/feature-gate-min-generic-const-args.rs:6:29 --> $DIR/feature-gate-min-generic-const-args.rs:8:29
| |
LL | fn foo<T: Trait>() -> [u8; <T as Trait>::ASSOC] { LL | fn foo<T: Trait>() -> [u8; <T as Trait>::ASSOC] {
| ^ cannot perform const operation using `T` | ^ cannot perform const operation using `T`
@ -7,5 +7,16 @@ LL | fn foo<T: Trait>() -> [u8; <T as Trait>::ASSOC] {
= note: type parameters may not be used in const expressions = note: type parameters may not be used in const expressions
= help: add `#![feature(generic_const_exprs)]` to allow generic const expressions = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions
error: aborting due to 1 previous error error[E0658]: the `#[type_const]` attribute is an experimental feature
--> $DIR/feature-gate-min-generic-const-args.rs:2:5
|
LL | #[type_const]
| ^^^^^^^^^^^^^
|
= note: see issue #132980 <https://github.com/rust-lang/rust/issues/132980> for more information
= help: add `#![feature(min_generic_const_args)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0658`.