Skip to content

Commit f9bae3b

Browse files
committed
Start lowering multi-segment const paths as ConstArgKind::Path
1 parent 613bdd4 commit f9bae3b

File tree

5 files changed

+165
-26
lines changed

5 files changed

+165
-26
lines changed

compiler/rustc_ast/src/ast.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,10 @@ impl Path {
124124
self.segments.first().is_some_and(|segment| segment.ident.name == kw::PathRoot)
125125
}
126126

127-
/// If this path is a single identifier with no arguments, does not ensure
128-
/// that the path resolves to a const param, the caller should check this.
129-
pub fn is_potential_trivial_const_arg(&self) -> bool {
130-
matches!(self.segments[..], [PathSegment { args: None, .. }])
127+
/// Does this path have no arguments, and if allow_multi_segment is false, is it a single segment?
128+
pub fn is_potential_trivial_const_arg(&self, allow_multi_segment: bool) -> bool {
129+
(allow_multi_segment || self.segments.len() == 1)
130+
&& self.segments.iter().all(|seg| seg.args.is_none())
131131
}
132132
}
133133

@@ -1177,18 +1177,19 @@ pub struct Expr {
11771177
}
11781178

11791179
impl Expr {
1180+
// FIXME: update docs
11801181
/// Could this expr be either `N`, or `{ N }`, where `N` is a const parameter.
11811182
///
11821183
/// If this is not the case, name resolution does not resolve `N` when using
11831184
/// `min_const_generics` as more complex expressions are not supported.
11841185
///
11851186
/// Does not ensure that the path resolves to a const param, the caller should check this.
11861187
/// This also does not consider macros, so it's only correct after macro-expansion.
1187-
pub fn is_potential_trivial_const_arg(&self) -> bool {
1188+
pub fn is_potential_trivial_const_arg(&self, allow_multi_segment: bool) -> bool {
11881189
let this = self.maybe_unwrap_block();
11891190

11901191
if let ExprKind::Path(None, path) = &this.kind
1191-
&& path.is_potential_trivial_const_arg()
1192+
&& path.is_potential_trivial_const_arg(allow_multi_segment)
11921193
{
11931194
true
11941195
} else {

compiler/rustc_ast_lowering/src/lib.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,7 +1107,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
11071107
.and_then(|partial_res| partial_res.full_res())
11081108
{
11091109
if !res.matches_ns(Namespace::TypeNS)
1110-
&& path.is_potential_trivial_const_arg()
1110+
// FIXME: should this only allow single-segment paths?
1111+
&& path.is_potential_trivial_const_arg(self.tcx.features().min_generic_const_args())
11111112
{
11121113
debug!(
11131114
"lower_generic_arg: Lowering type argument as const argument: {:?}",
@@ -2072,8 +2073,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
20722073
) -> &'hir hir::ConstArg<'hir> {
20732074
let tcx = self.tcx;
20742075

2075-
// FIXME(min_generic_const_args): we only allow one-segment const paths for now
2076-
let ct_kind = if path.is_potential_trivial_const_arg()
2076+
let ct_kind = if path
2077+
.is_potential_trivial_const_arg(tcx.features().min_generic_const_args())
20772078
&& (tcx.features().min_generic_const_args()
20782079
|| matches!(res, Res::Def(DefKind::ConstParam, _)))
20792080
{
@@ -2147,9 +2148,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
21472148
};
21482149
let maybe_res =
21492150
self.resolver.get_partial_res(expr.id).and_then(|partial_res| partial_res.full_res());
2150-
// FIXME(min_generic_const_args): we only allow one-segment const paths for now
21512151
if let ExprKind::Path(None, path) = &expr.kind
2152-
&& path.is_potential_trivial_const_arg()
2152+
&& path.is_potential_trivial_const_arg(tcx.features().min_generic_const_args())
21532153
&& (tcx.features().min_generic_const_args()
21542154
|| matches!(maybe_res, Some(Res::Def(DefKind::ConstParam, _))))
21552155
{

compiler/rustc_builtin_macros/src/format.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,8 @@ fn make_format_args(
190190
&& let [stmt] = block.stmts.as_slice()
191191
&& let StmtKind::Expr(expr) = &stmt.kind
192192
&& let ExprKind::Path(None, path) = &expr.kind
193-
&& path.is_potential_trivial_const_arg()
193+
&& path.segments.len() == 1
194+
&& path.segments[0].args.is_none()
194195
{
195196
err.multipart_suggestion(
196197
"quote your inlined format argument to use as string literal",

compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs

Lines changed: 140 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2110,11 +2110,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
21102110
let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself));
21112111
self.lower_const_path_resolved(opt_self_ty, path, hir_id)
21122112
}
2113-
hir::ConstArgKind::Path(qpath) => ty::Const::new_error_with_message(
2114-
tcx,
2115-
qpath.span(),
2116-
format!("Const::lower_const_arg: invalid qpath {qpath:?}"),
2117-
),
2113+
hir::ConstArgKind::Path(hir::QPath::TypeRelative(qself, segment)) => {
2114+
debug!(?qself, ?segment);
2115+
let ty = self.lower_ty(qself);
2116+
self.lower_const_assoc_path(hir_id, const_arg.span(), ty, qself, segment)
2117+
}
2118+
hir::ConstArgKind::Path(qpath @ hir::QPath::LangItem(..)) => {
2119+
ty::Const::new_error_with_message(
2120+
tcx,
2121+
qpath.span(),
2122+
format!("Const::lower_const_arg: invalid qpath {qpath:?}"),
2123+
)
2124+
}
21182125
hir::ConstArgKind::Anon(anon) => self.lower_anon_const(anon),
21192126
hir::ConstArgKind::Infer(span, ()) => self.ct_infer(None, span),
21202127
}
@@ -2203,8 +2210,134 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
22032210
}
22042211
}
22052212

2206-
/// Literals and const generic parameters are eagerly converted to a constant, everything else
2207-
/// becomes `Unevaluated`.
2213+
#[instrument(level = "debug", skip(self))]
2214+
pub fn lower_const_assoc_path(
2215+
&self,
2216+
hir_ref_id: HirId,
2217+
span: Span,
2218+
qself_ty: Ty<'tcx>,
2219+
qself: &'tcx hir::Ty<'tcx>,
2220+
assoc_segment: &'tcx hir::PathSegment<'tcx>,
2221+
) -> Const<'tcx> {
2222+
debug!(%qself_ty, ?assoc_segment.ident);
2223+
let tcx = self.tcx();
2224+
2225+
let assoc_ident = assoc_segment.ident;
2226+
2227+
// Check if we have an enum variant or an inherent associated const.
2228+
// FIXME(min_generic_const_args): handle assoc fns once we support those
2229+
if let Some(adt_def) = self.probe_adt(span, qself_ty) {
2230+
if adt_def.is_enum() {
2231+
let variant_def = adt_def
2232+
.variants()
2233+
.iter()
2234+
.find(|vd| tcx.hygienic_eq(assoc_ident, vd.ident(tcx), adt_def.did()));
2235+
if let Some(variant_def) = variant_def {
2236+
tcx.check_stability(variant_def.def_id, Some(hir_ref_id), span, None);
2237+
let _ = self.prohibit_generic_args(
2238+
slice::from_ref(assoc_segment).iter(),
2239+
GenericsArgsErrExtend::EnumVariant { qself, assoc_segment, adt_def },
2240+
);
2241+
let uv = ty::UnevaluatedConst::new(variant_def.def_id, ty::List::empty());
2242+
return Const::new_unevaluated(tcx, uv);
2243+
}
2244+
}
2245+
2246+
// FIXME(min_generic_const_args): Support self types other than ADTs.
2247+
let candidates = tcx
2248+
.inherent_impls(adt_def.did())
2249+
.iter()
2250+
.filter_map(|&impl_| {
2251+
self.probe_assoc_item(
2252+
assoc_ident,
2253+
ty::AssocKind::Const,
2254+
hir_ref_id,
2255+
span,
2256+
impl_,
2257+
)
2258+
})
2259+
.collect::<Vec<_>>();
2260+
match &candidates[..] {
2261+
[] => {}
2262+
[assoc] => return self.lower_assoc_const(span, assoc.def_id, assoc_segment),
2263+
[..] => {
2264+
return Const::new_error_with_message(tcx, span, "ambiguous assoc const path");
2265+
}
2266+
}
2267+
}
2268+
2269+
let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind {
2270+
path.res
2271+
} else {
2272+
Res::Err
2273+
};
2274+
2275+
// Find the type of the associated item, and the trait where the associated
2276+
// item is declared.
2277+
let bound_result = match (qself_ty.kind(), qself_res) {
2278+
(_, Res::SelfTyAlias { alias_to: impl_def_id, is_trait_impl: true, .. }) => {
2279+
// `Self` in an impl of a trait -- we have a concrete self type and a
2280+
// trait reference.
2281+
let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) else {
2282+
// A cycle error occurred, most likely.
2283+
self.dcx().span_bug(span, "expected cycle error");
2284+
};
2285+
2286+
self.probe_single_bound_for_assoc_item(
2287+
|| {
2288+
traits::supertraits(
2289+
tcx,
2290+
ty::Binder::dummy(trait_ref.instantiate_identity()),
2291+
)
2292+
},
2293+
AssocItemQSelf::SelfTyAlias,
2294+
ty::AssocKind::Const,
2295+
assoc_ident,
2296+
span,
2297+
None,
2298+
)
2299+
}
2300+
(
2301+
&ty::Param(_),
2302+
Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did),
2303+
) => self.probe_single_ty_param_bound_for_assoc_item(
2304+
param_did.expect_local(),
2305+
qself.span,
2306+
ty::AssocKind::Const,
2307+
assoc_ident,
2308+
span,
2309+
),
2310+
_ => panic!("handle errors here"), // TODO: do this
2311+
};
2312+
let bound = match bound_result {
2313+
Ok(b) => b,
2314+
Err(reported) => return Const::new_error(tcx, reported),
2315+
};
2316+
2317+
let trait_did = bound.def_id();
2318+
let assoc_const = self
2319+
.probe_assoc_item(assoc_ident, ty::AssocKind::Const, hir_ref_id, span, trait_did)
2320+
.expect("failed to find associated const");
2321+
self.lower_assoc_const(span, assoc_const.def_id, assoc_segment)
2322+
}
2323+
2324+
fn lower_assoc_const(
2325+
&self,
2326+
span: Span,
2327+
item_def_id: DefId,
2328+
item_segment: &hir::PathSegment<'tcx>,
2329+
) -> Const<'tcx> {
2330+
let tcx = self.tcx();
2331+
// FIXME: this is not necessarily correct.
2332+
// adapted from other code that also had a fixme about it being temporary.
2333+
let parent_args = ty::GenericArgs::identity_for_item(tcx, tcx.parent(item_def_id));
2334+
let args =
2335+
self.lower_generic_args_of_assoc_item(span, item_def_id, item_segment, parent_args);
2336+
let uv = ty::UnevaluatedConst::new(item_def_id, args);
2337+
Const::new_unevaluated(tcx, uv)
2338+
}
2339+
2340+
/// Literals are eagerly converted to a constant, everything else becomes `Unevaluated`.
22082341
#[instrument(skip(self), level = "debug")]
22092342
fn lower_anon_const(&self, anon: &AnonConst) -> Const<'tcx> {
22102343
let tcx = self.tcx();

compiler/rustc_resolve/src/late.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1181,7 +1181,7 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
11811181
if let TyKind::Path(None, ref path) = ty.kind
11821182
// We cannot disambiguate multi-segment paths right now as that requires type
11831183
// checking.
1184-
&& path.is_potential_trivial_const_arg()
1184+
&& path.is_potential_trivial_const_arg(false)
11851185
{
11861186
let mut check_ns = |ns| {
11871187
self.maybe_resolve_ident_in_lexical_scope(path.segments[0].ident, ns)
@@ -4576,11 +4576,12 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
45764576
constant, anon_const_kind
45774577
);
45784578

4579-
self.resolve_anon_const_manual(
4580-
constant.value.is_potential_trivial_const_arg(),
4581-
anon_const_kind,
4582-
|this| this.resolve_expr(&constant.value, None),
4583-
)
4579+
let is_trivial_const_arg = constant
4580+
.value
4581+
.is_potential_trivial_const_arg(self.r.tcx.features().min_generic_const_args());
4582+
self.resolve_anon_const_manual(is_trivial_const_arg, anon_const_kind, |this| {
4583+
this.resolve_expr(&constant.value, None)
4584+
})
45844585
}
45854586

45864587
/// There are a few places that we need to resolve an anon const but we did not parse an
@@ -4740,8 +4741,11 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
47404741
// Constant arguments need to be treated as AnonConst since
47414742
// that is how they will be later lowered to HIR.
47424743
if const_args.contains(&idx) {
4744+
let is_trivial_const_arg = argument.is_potential_trivial_const_arg(
4745+
self.r.tcx.features().min_generic_const_args(),
4746+
);
47434747
self.resolve_anon_const_manual(
4744-
argument.is_potential_trivial_const_arg(),
4748+
is_trivial_const_arg,
47454749
AnonConstKind::ConstArg(IsRepeatExpr::No),
47464750
|this| this.resolve_expr(argument, None),
47474751
);

0 commit comments

Comments
 (0)