Skip to content

Commit

Permalink
Introduce const Trait (always-const trait bounds)
Browse files Browse the repository at this point in the history
  • Loading branch information
fmease committed Dec 19, 2023
1 parent bf9229a commit 2abd167
Show file tree
Hide file tree
Showing 66 changed files with 522 additions and 241 deletions.
39 changes: 36 additions & 3 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,9 +300,22 @@ pub enum TraitBoundModifier {
/// `?Trait`
Maybe,

/// `const Trait`
Const(Span),

/// `~const Trait`
MaybeConst(Span),

/// `const !Trait`
//
// This parses but will be rejected during AST validation.
ConstNegative,

/// `const ?Trait`
//
// This parses but will be rejected during AST validation.
ConstMaybe,

/// `~const !Trait`
//
// This parses but will be rejected during AST validation.
Expand All @@ -315,10 +328,11 @@ pub enum TraitBoundModifier {
}

impl TraitBoundModifier {
pub fn to_constness(self) -> Const {
pub fn to_constness(self) -> BoundConstness {
match self {
Self::MaybeConst(span) => Const::Yes(span),
_ => Const::No,
Self::Const(span) => BoundConstness::Always(span),
Self::MaybeConst(span) => BoundConstness::Maybe(span),
_ => BoundConstness::Never,
}
}
}
Expand Down Expand Up @@ -2526,6 +2540,25 @@ pub enum BoundPolarity {
Maybe(Span),
}

#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
pub enum BoundConstness {
/// `Type: Trait`
Never,
/// `Type: const Trait`
Always(Span),
/// `Type: ~const Trait`
Maybe(Span),
}

impl From<Const> for BoundConstness {
fn from(constness: Const) -> Self {
match constness {
Const::Yes(span) => Self::Maybe(span),
Const::No => Self::Never,
}
}
}

#[derive(Clone, Encodable, Decodable, Debug)]
pub enum FnRetTy {
/// Returns type is not specified.
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_ast/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,8 @@ impl Token {
}

/// Returns `true` if the token can appear at the start of a generic bound.
// FIXME(const_trait_impl): This lacks `Tilde` and `kw::Const` but everythings seems to work
// regardless for some reason?
pub fn can_begin_bound(&self) -> bool {
self.is_path_start()
|| self.is_lifetime()
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.lower_generics(ast_generics, *constness, id, &itctx, |this| {
let trait_ref = trait_ref.as_ref().map(|trait_ref| {
this.lower_trait_ref(
*constness,
(*constness).into(),
trait_ref,
&ImplTraitContext::Disallowed(ImplTraitPosition::Trait),
)
Expand Down
86 changes: 54 additions & 32 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1318,7 +1318,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
span: t.span,
},
itctx,
ast::Const::No,
ast::BoundConstness::Never,
);
let bounds = this.arena.alloc_from_iter([bound]);
let lifetime_bound = this.elided_dyn_bound(t.span);
Expand Down Expand Up @@ -1426,18 +1426,21 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
GenericBound::Trait(
ty,
modifier @ (TraitBoundModifier::None
| TraitBoundModifier::Const(_)
| TraitBoundModifier::MaybeConst(_)
| TraitBoundModifier::Negative),
) => {
Some(this.lower_poly_trait_ref(ty, itctx, modifier.to_constness()))
}
// `~const ?Bound` will cause an error during AST validation
// `{,~}const ?Bound` will cause an error during AST validation
// anyways, so treat it like `?Bound` as compilation proceeds.
GenericBound::Trait(
_,
TraitBoundModifier::Maybe
| TraitBoundModifier::MaybeConstMaybe
| TraitBoundModifier::MaybeConstNegative,
| TraitBoundModifier::MaybeConstNegative
| TraitBoundModifier::ConstMaybe
| TraitBoundModifier::ConstNegative,
) => None,
GenericBound::Outlives(lifetime) => {
if lifetime_bound.is_none() {
Expand Down Expand Up @@ -2176,7 +2179,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {

fn lower_trait_ref(
&mut self,
constness: ast::Const,
constness: ast::BoundConstness,
p: &TraitRef,
itctx: &ImplTraitContext,
) -> hir::TraitRef<'hir> {
Expand All @@ -2199,7 +2202,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
&mut self,
p: &PolyTraitRef,
itctx: &ImplTraitContext,
constness: ast::Const,
constness: ast::BoundConstness,
) -> hir::PolyTraitRef<'hir> {
let bound_generic_params =
self.lower_lifetime_binder(p.trait_ref.ref_id, &p.bound_generic_params);
Expand Down Expand Up @@ -2323,6 +2326,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
fn lower_trait_bound_modifier(&mut self, f: TraitBoundModifier) -> hir::TraitBoundModifier {
match f {
TraitBoundModifier::None => hir::TraitBoundModifier::None,
TraitBoundModifier::Const(_) => hir::TraitBoundModifier::Const,
TraitBoundModifier::MaybeConst(_) => hir::TraitBoundModifier::MaybeConst,

TraitBoundModifier::Negative => {
Expand All @@ -2333,12 +2337,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
}

// `MaybeConstMaybe` will cause an error during AST validation, but we need to pick a
// `{,Maybe}ConstMaybe` will cause an error during AST validation, but we need to pick a
// placeholder for compilation to proceed.
TraitBoundModifier::MaybeConstMaybe | TraitBoundModifier::Maybe => {
hir::TraitBoundModifier::Maybe
}
TraitBoundModifier::ConstMaybe
| TraitBoundModifier::MaybeConstMaybe
| TraitBoundModifier::Maybe => hir::TraitBoundModifier::Maybe,
TraitBoundModifier::MaybeConstNegative => hir::TraitBoundModifier::MaybeConst,
TraitBoundModifier::ConstNegative => hir::TraitBoundModifier::Const,
}
}

Expand Down Expand Up @@ -2556,45 +2561,62 @@ struct GenericArgsCtor<'hir> {
}

impl<'hir> GenericArgsCtor<'hir> {
fn push_constness(&mut self, lcx: &mut LoweringContext<'_, 'hir>, constness: ast::Const) {
fn push_constness(
&mut self,
lcx: &mut LoweringContext<'_, 'hir>,
constness: ast::BoundConstness,
) {
if !lcx.tcx.features().effects {
return;
}

// if bound is non-const, don't add host effect param
let ast::Const::Yes(span) = constness else { return };
let (span, body) = match constness {
BoundConstness::Never => return,
BoundConstness::Always(span) => {
let span = lcx.lower_span(span);

let span = lcx.lower_span(span);
let body = hir::ExprKind::Lit(
lcx.arena.alloc(hir::Lit { node: LitKind::Bool(false), span }),
);

let id = lcx.next_node_id();
let hir_id = lcx.next_id();
(span, body)
}
BoundConstness::Maybe(span) => {
let span = lcx.lower_span(span);

let Some(host_param_id) = lcx.host_param_id else {
lcx.tcx.sess.span_delayed_bug(
span,
"no host param id for call in const yet no errors reported",
);
return;
};
let Some(host_param_id) = lcx.host_param_id else {
lcx.tcx.sess.span_delayed_bug(
span,
"no host param id for call in const yet no errors reported",
);
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(
let body = 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)],
segments: arena_vec![
lcx;
hir::PathSegment::new(
Ident { name: sym::host, span },
hir_id,
res
)
],
}),
));
lcx.expr(span, expr_kind)
})
});

(span, body)
}
};
let body = lcx.lower_body(|lcx| (&[], lcx.expr(span, body)));

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

let def_id = lcx.create_def(
lcx.current_hir_id_owner.def_id,
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_ast_lowering/src/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
param_mode: ParamMode,
itctx: &ImplTraitContext,
// constness of the impl/bound if this is a trait path
constness: Option<ast::Const>,
constness: Option<ast::BoundConstness>,
) -> hir::QPath<'hir> {
let qself_position = qself.as_ref().map(|q| q.position);
let qself = qself.as_ref().map(|q| self.lower_ty(&q.ty, itctx));
Expand Down Expand Up @@ -179,7 +179,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
param_mode: ParamMode,
parenthesized_generic_args: ParenthesizedGenericArgs,
itctx: &ImplTraitContext,
constness: Option<ast::Const>,
constness: Option<ast::BoundConstness>,
) -> hir::PathSegment<'hir> {
debug!("path_span: {:?}, lower_path_segment(segment: {:?})", path_span, segment);
let (mut generic_args, infer_args) = if let Some(generic_args) = segment.args.as_deref() {
Expand Down
6 changes: 4 additions & 2 deletions compiler/rustc_ast_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ ast_passes_const_and_c_variadic = functions cannot be both `const` and C-variadi
.const = `const` because of this
.variadic = C-variadic because of this
ast_passes_const_bound_trait_object = const trait bounds are not allowed in trait object types
ast_passes_const_without_body =
free constant item without body
.suggestion = provide a definition for the constant
Expand Down Expand Up @@ -181,6 +183,8 @@ ast_passes_match_arm_with_no_body =
ast_passes_module_nonascii = trying to load file for module `{$name}` with non-ascii identifier name
.help = consider using the `#[path]` attribute to specify filesystem path
ast_passes_mutually_exclusive_trait_bound_modifiers = `{$left}` and `{$right}` are mutually exclusive
ast_passes_negative_bound_not_supported =
negative bounds are not supported
Expand All @@ -195,8 +199,6 @@ ast_passes_nomangle_ascii = `#[no_mangle]` requires ASCII identifier
ast_passes_obsolete_auto = `impl Trait for .. {"{}"}` is an obsolete syntax
.help = use `auto trait Trait {"{}"}` instead
ast_passes_optional_const_exclusive = `~const` and `{$modifier}` are mutually exclusive
ast_passes_optional_trait_object = `?Trait` is not permitted in trait object types
ast_passes_optional_trait_supertrait = `?Trait` is not permitted in supertraits
Expand Down
30 changes: 21 additions & 9 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1207,6 +1207,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
(BoundKind::TraitObject, TraitBoundModifier::Maybe) => {
self.dcx().emit_err(errors::OptionalTraitObject { span: poly.span });
}
(BoundKind::TraitObject, TraitBoundModifier::Const(_)) => {
self.dcx().emit_err(errors::ConstBoundTraitObject { span: poly.span });
}
(_, &TraitBoundModifier::MaybeConst(span))
if let Some(reason) = &self.disallow_tilde_const =>
{
Expand Down Expand Up @@ -1235,16 +1238,25 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
};
self.dcx().emit_err(errors::TildeConstDisallowed { span, reason });
}
(_, TraitBoundModifier::MaybeConstMaybe) => {
self.dcx().emit_err(errors::OptionalConstExclusive {
span: bound.span(),
modifier: "?",
});
}
(_, TraitBoundModifier::MaybeConstNegative) => {
self.dcx().emit_err(errors::OptionalConstExclusive {
(
_,
TraitBoundModifier::ConstMaybe
| TraitBoundModifier::ConstNegative
| TraitBoundModifier::MaybeConstMaybe
| TraitBoundModifier::MaybeConstNegative,
) => {
let (left, right) = match modify {
TraitBoundModifier::ConstMaybe => ("const", "?"),
TraitBoundModifier::ConstNegative => ("const", "!"),
TraitBoundModifier::MaybeConstMaybe => ("~const", "?"),
TraitBoundModifier::MaybeConstNegative => ("~const", "!"),
_ => unreachable!("unexpected trait bound modifier"),
};

self.dcx().emit_err(errors::MutuallyExclusiveTraitBoundModifiers {
span: bound.span(),
modifier: "!",
left,
right,
});
}
_ => {}
Expand Down
14 changes: 11 additions & 3 deletions compiler/rustc_ast_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,13 @@ pub struct OptionalTraitObject {
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(ast_passes_const_bound_trait_object)]
pub struct ConstBoundTraitObject {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(ast_passes_tilde_const_disallowed)]
pub struct TildeConstDisallowed {
Expand Down Expand Up @@ -580,11 +587,12 @@ pub enum TildeConstReason {
}

#[derive(Diagnostic)]
#[diag(ast_passes_optional_const_exclusive)]
pub struct OptionalConstExclusive {
#[diag(ast_passes_mutually_exclusive_trait_bound_modifiers)]
pub struct MutuallyExclusiveTraitBoundModifiers {
#[primary_span]
pub span: Span,
pub modifier: &'static str,
pub left: &'static str,
pub right: &'static str,
}

#[derive(Diagnostic)]
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1567,6 +1567,17 @@ impl<'a> State<'a> {
TraitBoundModifier::Maybe => {
self.word("?");
}
TraitBoundModifier::Const(_) => {
self.word_space("const");
}
TraitBoundModifier::ConstNegative => {
self.word_space("const");
self.word("!");
}
TraitBoundModifier::ConstMaybe => {
self.word_space("const");
self.word("?");
}
TraitBoundModifier::MaybeConst(_) => {
self.word_space("~const");
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ pub enum TraitBoundModifier {
None,
Negative,
Maybe,
Const,
MaybeConst,
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ hir_analysis_coercion_between_struct_same_note = expected coercion between the s
hir_analysis_coercion_between_struct_single_note = expected a single field to be coerced, none found
hir_analysis_const_bound_for_non_const_trait =
~const can only be applied to `#[const_trait]` traits
{$modifier} can only be applied to `#[const_trait]` traits
hir_analysis_const_impl_for_non_const_trait =
const `impl` for trait `{$trait_name}` which is not marked with `#[const_trait]`
Expand Down
Loading

0 comments on commit 2abd167

Please sign in to comment.