Skip to content

mgca: Lower all const paths as ConstArgKind::Path #135186

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 33 additions & 15 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,19 @@ impl Path {
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
/// that the path resolves to a const param, the caller should check this.
pub fn is_potential_trivial_const_arg(&self) -> bool {
matches!(self.segments[..], [PathSegment { args: None, .. }])
/// Check if this path is potentially a trivial const arg, i.e., one that can _potentially_
/// be represented without an anon const in the HIR.
///
/// 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())
}
}

Expand Down Expand Up @@ -1208,22 +1217,31 @@ pub struct 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.
///
/// This will unwrap at most one block level (curly braces). After that, if the expression
/// is a path, it mostly dispatches to [`Path::is_potential_trivial_const_arg`].
/// See there for more info about `allow_mgca_arg`.
///
/// If this is not the case, name resolution does not resolve `N` when using
/// `min_const_generics` as more complex expressions are not supported.
/// 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, the caller should check this.
/// 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.
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();

if let ExprKind::Path(None, path) = &this.kind
&& path.is_potential_trivial_const_arg()
{
true
if allow_mgca_arg {
matches!(this.kind, ExprKind::Path(..))
} else {
false
if let ExprKind::Path(None, path) = &this.kind
&& path.is_potential_trivial_const_arg(allow_mgca_arg)
{
true
} else {
false
}
}
}

Expand Down
17 changes: 8 additions & 9 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1094,7 +1094,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
.and_then(|partial_res| partial_res.full_res())
{
if !res.matches_ns(Namespace::TypeNS)
&& path.is_potential_trivial_const_arg()
&& path.is_potential_trivial_const_arg(false)
{
debug!(
"lower_generic_arg: Lowering type argument as const argument: {:?}",
Expand Down Expand Up @@ -2061,8 +2061,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
) -> &'hir hir::ConstArg<'hir> {
let tcx = self.tcx;

// FIXME(min_generic_const_args): we only allow one-segment const paths for now
let ct_kind = if path.is_potential_trivial_const_arg()
let ct_kind = if path
.is_potential_trivial_const_arg(tcx.features().min_generic_const_args())
&& (tcx.features().min_generic_const_args()
|| matches!(res, Res::Def(DefKind::ConstParam, _)))
{
Expand All @@ -2072,7 +2072,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
path,
ParamMode::Optional,
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),
None,
);
Expand Down Expand Up @@ -2136,19 +2136,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
};
let maybe_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(None, path) = &expr.kind
&& path.is_potential_trivial_const_arg()
if let ExprKind::Path(qself, path) = &expr.kind
&& 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, _))))
{
let qpath = self.lower_qpath(
expr.id,
&None,
qself,
path,
ParamMode::Optional,
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),
None,
);
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_builtin_macros/src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@ fn make_format_args(
&& let [stmt] = block.stmts.as_slice()
&& let StmtKind::Expr(expr) = &stmt.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(
"quote your inlined format argument to use as string literal",
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,13 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
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:
// ==========================================================================
Expand Down
28 changes: 14 additions & 14 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ use tracing::{debug, instrument};

use crate::check::intrinsic::intrinsic_operation_unsafety;
use crate::errors;
use crate::hir_ty_lowering::errors::assoc_kind_str;
use crate::hir_ty_lowering::{FeedConstTy, HirTyLowerer, RegionInferReason};

pub(crate) mod dump;
Expand Down Expand Up @@ -443,21 +444,22 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
self.tcx.at(span).type_param_predicates((self.item_def_id, def_id, assoc_name))
}

fn lower_assoc_ty(
fn lower_assoc_shared(
&self,
span: Span,
item_def_id: DefId,
item_segment: &hir::PathSegment<'tcx>,
item_segment: &rustc_hir::PathSegment<'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() {
let item_args = self.lowerer().lower_generic_args_of_assoc_item(
span,
item_def_id,
item_segment,
trait_ref.args,
);
Ty::new_projection_from_args(self.tcx(), item_def_id, item_args)
Ok((item_def_id, item_args))
} else {
// There are no late-bound regions; we can just ignore the binder.
let (mut mpart_sugg, mut inferred_sugg) = (None, None);
Expand Down Expand Up @@ -518,16 +520,14 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
}
_ => {}
}
Ty::new_error(
self.tcx(),
self.tcx().dcx().emit_err(errors::AssociatedItemTraitUninferredGenericParams {
span,
inferred_sugg,
bound,
mpart_sugg,
what: "type",
}),
)

Err(self.tcx().dcx().emit_err(errors::AssociatedItemTraitUninferredGenericParams {
span,
inferred_sugg,
bound,
mpart_sugg,
what: assoc_kind_str(kind),
}))
}
}

Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -468,11 +468,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {

// Good error for `where Trait::method(..): Send`.
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,
hir_ty.span,
item_segment,
ty::AssocKind::Type,
);
return Ty::new_error(tcx, guar);
};
let self_ty = self.lower_ty(self_ty);

Expand Down
36 changes: 22 additions & 14 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -385,14 +385,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
})
}

pub(super) fn report_ambiguous_assoc_ty(
pub(super) fn report_ambiguous_assoc(
&self,
span: Span,
types: &[String],
traits: &[String],
name: Symbol,
kind: ty::AssocKind,
) -> 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
.tcx()
.resolutions(())
Expand All @@ -417,7 +420,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
span,
format!(
"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",
),
format!("<Type as Trait>::{name}"),
Expand All @@ -440,7 +443,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
span,
format!(
"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",
),
traits.iter().map(|trait_str| format!("<Example as {trait_str}>::{name}")),
Expand All @@ -451,7 +454,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
err.span_suggestion_verbose(
span,
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",
),
format!("<{type_str} as Example>::{name}"),
Expand All @@ -462,7 +465,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
err.span_suggestions(
span,
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 \
path",
),
Expand Down Expand Up @@ -491,7 +494,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
err.emit()
}

pub(crate) fn complain_about_ambiguous_inherent_assoc_ty(
pub(crate) fn complain_about_ambiguous_inherent_assoc(
&self,
name: Ident,
candidates: Vec<DefId>,
Expand Down Expand Up @@ -552,13 +555,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}

// 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,
name: Ident,
self_ty: Ty<'tcx>,
candidates: Vec<(DefId, (DefId, DefId))>,
fulfillment_errors: Vec<FulfillmentError<'tcx>>,
span: Span,
kind: ty::AssocKind,
) -> ErrorGuaranteed {
// FIXME(fmease): This was copied in parts from an old version of `rustc_hir_typeck::method::suggest`.
// Either
Expand All @@ -568,12 +572,16 @@ impl<'tcx> dyn HirTyLowerer<'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 add_def_label = |err: &mut Diag<'_>| {
if let Some(did) = adt_did {
err.span_label(
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)
),
);
}
};
Expand All @@ -600,11 +608,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
self.dcx(),
name.span,
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.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);
return err.emit();
Expand Down Expand Up @@ -685,7 +693,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {

let mut err = self.dcx().struct_span_err(
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() {
err.note(format!(
Expand All @@ -695,7 +703,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
err.span_label(
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 {
Expand Down Expand Up @@ -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 {
ty::AssocKind::Fn => "function",
ty::AssocKind::Const => "constant",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ fn generic_arg_mismatch_err(
body.value.kind
&& 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
err.help(format!("`{}` is a function item, not a type", tcx.item_name(id)));
err.help("function item types cannot be named directly");
Expand Down
Loading
Loading