Skip to content

Commit

Permalink
effects: support generic const items
Browse files Browse the repository at this point in the history
  • Loading branch information
fmease committed Nov 3, 2023
1 parent ffb7ed9 commit f47ca54
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 59 deletions.
81 changes: 56 additions & 25 deletions compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::errors::{InvalidAbi, InvalidAbiReason, InvalidAbiSuggestion, MisplacedRelaxTraitBound};
use super::ResolverAstLoweringExt;
use super::{AstOwner, ImplTraitContext, ImplTraitPosition};
use super::{FnDeclKind, LoweringContext, ParamMode};
use super::{EffectContext, FnDeclKind, LoweringContext, ParamMode};

use hir::definitions::DefPathData;
use rustc_ast::ptr::P;
Expand Down Expand Up @@ -90,7 +90,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
allow_try_trait: Some([sym::try_trait_v2, sym::yeet_desugar_details][..].into()),
allow_gen_future,
generics_def_id_map: Default::default(),
host_param_id: None,
effect_context: None,
};
lctx.with_hir_id_owner(owner, |lctx| f(lctx));

Expand Down Expand Up @@ -150,7 +150,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
lctx.is_in_trait_impl = impl_.of_trait.is_some();
}
hir::ItemKind::Trait(_, _, generics, _, _) if lctx.tcx.features().effects => {
lctx.host_param_id = generics
lctx.effect_context = generics
.params
.iter()
.find(|param| {
Expand All @@ -160,7 +160,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
.iter()
.any(|attr| attr.has_name(sym::rustc_host))
})
.map(|param| param.def_id);
.map(|param| EffectContext::Parametrized { host_param_id: param.def_id });
}
_ => {}
}
Expand Down Expand Up @@ -256,7 +256,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
ItemKind::Const(box ast::ConstItem { generics, ty, expr, .. }) => {
let (generics, (ty, body_id)) = self.lower_generics(
generics,
Const::No,
Constness::Always,
id,
&ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| this.lower_const_item(ty, span, expr.as_deref()),
Expand Down Expand Up @@ -286,11 +286,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
);

let itctx = ImplTraitContext::Universal;
let (generics, decl) =
this.lower_generics(generics, header.constness, id, &itctx, |this| {
let (generics, decl) = this.lower_generics(
generics,
header.constness.into(),
id,
&itctx,
|this| {
let ret_id = asyncness.opt_return_id();
this.lower_fn_decl(&decl, id, *fn_sig_span, FnDeclKind::Fn, ret_id)
});
},
);
let sig = hir::FnSig {
decl,
header: this.lower_fn_header(*header),
Expand Down Expand Up @@ -325,7 +330,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
add_ty_alias_where_clause(&mut generics, *where_clauses, true);
let (generics, ty) = self.lower_generics(
&generics,
Const::No,
Constness::Never,
id,
&ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| match ty {
Expand All @@ -347,7 +352,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
ItemKind::Enum(enum_definition, generics) => {
let (generics, variants) = self.lower_generics(
generics,
Const::No,
Constness::Never,
id,
&ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
Expand All @@ -361,7 +366,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
ItemKind::Struct(struct_def, generics) => {
let (generics, struct_def) = self.lower_generics(
generics,
Const::No,
Constness::Never,
id,
&ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| this.lower_variant_data(hir_id, struct_def),
Expand All @@ -371,7 +376,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
ItemKind::Union(vdata, generics) => {
let (generics, vdata) = self.lower_generics(
generics,
Const::No,
Constness::Never,
id,
&ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| this.lower_variant_data(hir_id, vdata),
Expand Down Expand Up @@ -403,7 +408,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
// parent lifetime.
let itctx = ImplTraitContext::Universal;
let (generics, (trait_ref, lowered_ty)) =
self.lower_generics(ast_generics, *constness, id, &itctx, |this| {
self.lower_generics(ast_generics, (*constness).into(), id, &itctx, |this| {
let trait_ref = trait_ref.as_ref().map(|trait_ref| {
this.lower_trait_ref(
*constness,
Expand Down Expand Up @@ -449,7 +454,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
.unwrap_or(&[])
.iter()
.find(|x| x.has_name(sym::const_trait))
.map_or(Const::No, |x| Const::Yes(x.span));
.map_or(Constness::Never, |x| Constness::Maybe(x.span));
let (generics, (unsafety, items, bounds)) = self.lower_generics(
generics,
constness,
Expand All @@ -472,7 +477,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
ItemKind::TraitAlias(generics, bounds) => {
let (generics, bounds) = self.lower_generics(
generics,
Const::No,
Constness::Never,
id,
&ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
Expand Down Expand Up @@ -627,7 +632,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let fdec = &sig.decl;
let itctx = ImplTraitContext::Universal;
let (generics, (fn_dec, fn_args)) =
self.lower_generics(generics, Const::No, i.id, &itctx, |this| {
self.lower_generics(generics, Constness::Never, i.id, &itctx, |this| {
(
// Disallow `impl Trait` in foreign items.
this.lower_fn_decl(
Expand Down Expand Up @@ -746,7 +751,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => {
let (generics, kind) = self.lower_generics(
&generics,
Const::No,
Constness::Always,
i.id,
&ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
Expand Down Expand Up @@ -791,7 +796,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
add_ty_alias_where_clause(&mut generics, *where_clauses, false);
let (generics, kind) = self.lower_generics(
&generics,
Const::No,
Constness::Never,
i.id,
&ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
Expand Down Expand Up @@ -859,7 +864,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let (generics, kind) = match &i.kind {
AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => self.lower_generics(
&generics,
Const::No,
Constness::Always,
i.id,
&ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
Expand Down Expand Up @@ -895,7 +900,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
add_ty_alias_where_clause(&mut generics, *where_clauses, false);
self.lower_generics(
&generics,
Const::No,
Constness::Never,
i.id,
&ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| match ty {
Expand Down Expand Up @@ -1255,7 +1260,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let header = self.lower_fn_header(sig.header);
let itctx = ImplTraitContext::Universal;
let (generics, decl) =
self.lower_generics(generics, sig.header.constness, id, &itctx, |this| {
self.lower_generics(generics, sig.header.constness.into(), id, &itctx, |this| {
this.lower_fn_decl(&sig.decl, id, sig.span, kind, is_async)
});
(generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) })
Expand Down Expand Up @@ -1333,7 +1338,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn lower_generics<T>(
&mut self,
generics: &Generics,
constness: Const,
constness: Constness,
parent_node_id: NodeId,
itctx: &ImplTraitContext,
f: impl FnOnce(&mut Self) -> T,
Expand Down Expand Up @@ -1387,7 +1392,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
// Desugar `~const` bound in generics into an additional `const host: bool` param
// if the effects feature is enabled. This needs to be done before we lower where
// clauses since where clauses need to bind to the DefId of the host param
let host_param_parts = if let Const::Yes(span) = constness
let host_param_parts = if let Constness::Maybe(span) = constness
&& self.tcx.features().effects
{
if let Some(param) =
Expand All @@ -1396,7 +1401,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
// user has manually specified a `rustc_host` param, in this case, we set
// the param id so that lowering logic can use that. But we don't create
// another host param, so this gives `None`.
self.host_param_id = Some(self.local_def_id(param.id));
self.effect_context = Some(EffectContext::Parametrized {
host_param_id: self.local_def_id(param.id),
});
None
} else {
let param_node_id = self.next_node_id();
Expand All @@ -1407,13 +1414,21 @@ impl<'hir> LoweringContext<'_, 'hir> {
DefPathData::TypeNs(sym::host),
span,
);
self.host_param_id = Some(def_id);
self.effect_context = Some(EffectContext::Parametrized {
host_param_id: def_id,
});
Some((span, hir_id, def_id))
}
} else {
None
};

// FIXME(generic_const_items, effects): Instead of interpreting `~const` bounds as `const`
// bounds, we could introduce `const` bounds into the surface language.
if let Constness::Always = constness && self.tcx.features().effects {
self.effect_context = Some(EffectContext::Invariant { value: false });
};

let mut predicates: SmallVec<[hir::WherePredicate<'hir>; 4]> = SmallVec::new();
predicates.extend(generics.params.iter().filter_map(|param| {
self.lower_generic_bound_predicate(
Expand Down Expand Up @@ -1660,3 +1675,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
}
}

#[derive(Debug)]
enum Constness {
Always,
Maybe(Span),
Never,
}

impl From<Const> for Constness {
fn from(constness: Const) -> Self {
match constness {
Const::Yes(span) => Self::Maybe(span),
Const::No => Self::Never,
}
}
}
48 changes: 29 additions & 19 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ struct LoweringContext<'a, 'hir> {
/// field from the original parameter 'a to the new parameter 'a1.
generics_def_id_map: Vec<FxHashMap<LocalDefId, LocalDefId>>,

host_param_id: Option<LocalDefId>,
effect_context: Option<EffectContext>,
}

trait ResolverAstLoweringExt {
Expand Down Expand Up @@ -227,6 +227,12 @@ impl ResolverAstLoweringExt for ResolverAstLowering {
}
}

#[derive(Clone, Copy)]
enum EffectContext {
Parametrized { host_param_id: LocalDefId },
Invariant { value: bool },
}

/// Context of `impl Trait` in code, which determines whether it is allowed in an HIR subtree,
/// and if so, what meaning it has.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -2476,34 +2482,38 @@ impl<'hir> GenericArgsCtor<'hir> {

// if bound is non-const, don't add host effect param
let ast::Const::Yes(span) = constness else { return };

let span = lcx.lower_span(span);

let id = lcx.next_node_id();
let hir_id = lcx.next_id();

let Some(host_param_id) = lcx.host_param_id else {
lcx.tcx
.sess
.delay_span_bug(span, "no host param id for call in const yet no errors reported");
let Some(context) = lcx.effect_context else {
lcx.tcx.sess.delay_span_bug(span, "no effect context for maybe-const trait bound");
return;
};

let body = lcx.lower_body(|lcx| {
(&[], {
let hir_id = lcx.next_id();
let res = Res::Def(DefKind::ConstParam, host_param_id.to_def_id());
let expr_kind = hir::ExprKind::Path(hir::QPath::Resolved(
None,
lcx.arena.alloc(hir::Path {
span,
res,
segments: arena_vec![lcx; hir::PathSegment::new(Ident {
name: sym::host,
span,
}, hir_id, res)],
}),
));
let expr_kind = match context {
EffectContext::Parametrized { host_param_id } => {
let hir_id = lcx.next_id();
let res = Res::Def(DefKind::ConstParam, host_param_id.to_def_id());
hir::ExprKind::Path(hir::QPath::Resolved(
None,
lcx.arena.alloc(hir::Path {
span,
res,
segments: arena_vec![lcx; hir::PathSegment::new(Ident {
name: sym::host,
span,
}, hir_id, res)],
}),
))
}
EffectContext::Invariant { value } => hir::ExprKind::Lit(
lcx.arena.alloc(hir::Lit { node: LitKind::Bool(value), span }),
),
};
lcx.expr(span, expr_kind)
})
});
Expand Down
17 changes: 13 additions & 4 deletions tests/ui/generic-const-items/const-trait-impl.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
// known-bug: #110395
// FIXME check-pass
// check-pass

// Test that we can call methods from const trait impls inside of generic const items.
// `~const` bounds are interpreted as `const` bounds.

#![feature(generic_const_items, const_trait_impl)]
#![feature(generic_const_items, const_trait_impl, effects)]
#![allow(incomplete_features)]
#![crate_type = "lib"]

// FIXME(generic_const_items): Interpret `~const` as always-const.
const CREATE<T: ~const Create>: T = T::create();

pub const K0: i32 = CREATE::<i32>;
pub const K1: i32 = CREATE; // arg inferred

trait Mod { // doesn't need to be a `#[const_trait]`
const CREATE<T: ~const Create>: T;
}

impl Mod for () {
const CREATE<T: ~const Create>: T = T::create();
}

pub const K2: i32 = <() as Mod>::CREATE::<i32>;

#[const_trait]
trait Create {
fn create() -> Self;
Expand Down
11 changes: 0 additions & 11 deletions tests/ui/generic-const-items/const-trait-impl.stderr

This file was deleted.

0 comments on commit f47ca54

Please sign in to comment.