Skip to content
Open
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
11 changes: 11 additions & 0 deletions compiler/rustc_ast_lowering/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@ use rustc_errors::{DiagArgFromDisplay, DiagSymbolList};
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_span::{Ident, Span, Symbol};

#[derive(Diagnostic)]
#[diag("a function cannot be both `comptime` and `const`")]
pub(crate) struct ConstComptimeFn {
#[primary_span]
#[suggestion("remove the `const`", applicability = "machine-applicable", code = "")]
#[note("`const` implies the function can be called at runtime, too")]
pub span: Span,
#[label("`comptime` because of this")]
pub attr_span: Span,
}

#[derive(Diagnostic)]
#[diag("parenthesized type parameters may only be used with a `Fn` trait", code = E0214)]
pub(crate) struct GenericTypeWithParentheses {
Expand Down
24 changes: 18 additions & 6 deletions compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use super::{
AstOwner, FnDeclKind, GenericArgsMode, ImplTraitContext, ImplTraitPosition, LoweringContext,
ParamMode, RelaxedBoundForbiddenReason, RelaxedBoundPolicy, ResolverAstLoweringExt,
};
use crate::errors::ConstComptimeFn;

/// Wraps either IndexVec (during `hir_crate`), which acts like a primary
/// storage for most of the MaybeOwners, or FxIndexMap during delayed AST -> HIR
Expand Down Expand Up @@ -1750,12 +1751,23 @@ impl<'hir> LoweringContext<'_, 'hir> {
safety.into()
};

hir::FnHeader {
safety,
asyncness,
constness: self.lower_constness(h.constness),
abi: self.lower_extern(h.ext),
let mut constness = self.lower_constness(h.constness);
if let Some(&attr_span) = find_attr!(attrs, Comptime(span) => span) {
match std::mem::replace(&mut constness, rustc_hir::Constness::Const { always: true }) {
rustc_hir::Constness::Const { always: true } => {
unreachable!("lower_constness cannot produce comptime")
}
// A function can't be `const` and `comptime` at the same time
rustc_hir::Constness::Const { always: false } => {
let Const::Yes(span) = h.constness else { unreachable!() };
self.dcx().emit_err(ConstComptimeFn { span, attr_span });
}
// Good
rustc_hir::Constness::NotConst => {}
}
}

hir::FnHeader { safety, asyncness, constness, abi: self.lower_extern(h.ext) }
}

pub(super) fn lower_abi(&mut self, abi_str: StrLit) -> ExternAbi {
Expand Down Expand Up @@ -1817,7 +1829,7 @@ impl<'hir> LoweringContext<'_, 'hir> {

pub(super) fn lower_constness(&mut self, c: Const) -> hir::Constness {
match c {
Const::Yes(_) => hir::Constness::Const,
Const::Yes(_) => hir::Constness::Const { always: false },
Const::No => hir::Constness::NotConst,
}
}
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/semantics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,14 @@ impl NoArgsAttributeParser for MayDangleParser {
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); //FIXME Still checked fully in `check_attr.rs`
const CREATE: fn(span: Span) -> AttributeKind = AttributeKind::MayDangle;
}

pub(crate) struct ComptimeParser;
impl NoArgsAttributeParser for ComptimeParser {
const PATH: &[Symbol] = &[sym::rustc_comptime];
const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Method(MethodKind::Inherent)),
Allow(Target::Fn),
]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::Comptime;
}
3 changes: 2 additions & 1 deletion compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ use crate::attributes::repr::*;
use crate::attributes::rustc_allocator::*;
use crate::attributes::rustc_dump::*;
use crate::attributes::rustc_internal::*;
use crate::attributes::semantics::*;
use crate::attributes::semantics::{ComptimeParser, *};
use crate::attributes::stability::*;
use crate::attributes::test_attrs::*;
use crate::attributes::traits::*;
Expand Down Expand Up @@ -231,6 +231,7 @@ attribute_parsers!(
Single<WithoutArgs<AutomaticallyDerivedParser>>,
Single<WithoutArgs<ColdParser>>,
Single<WithoutArgs<CompilerBuiltinsParser>>,
Single<WithoutArgs<ComptimeParser>>,
Single<WithoutArgs<ConstContinueParser>>,
Single<WithoutArgs<CoroutineParser>>,
Single<WithoutArgs<DefaultLibAllocatorParser>>,
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_const_eval/src/check_consts/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -775,7 +775,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
// to do different checks than usual.

trace!("attempting to call a trait method");
let is_const = tcx.constness(callee) == hir::Constness::Const;
let is_const =
matches!(tcx.constness(callee), hir::Constness::Const { always: false });

// Only consider a trait to be const if the const conditions hold.
// Otherwise, it's really misleading to call something "conditionally"
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_const_eval/src/check_consts/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,8 @@ fn build_error_for_const_call<'tcx>(
err.help("const traits are not yet supported on stable Rust");
}
}
} else if ccx.tcx.constness(callee) != hir::Constness::Const {
} else if !matches!(ccx.tcx.constness(callee), hir::Constness::Const { always: false })
{
let name = ccx.tcx.item_name(callee);
err.span_note(
ccx.tcx.def_span(callee),
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_const_eval/src/const_eval/fn_queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Constness {
let node = tcx.hir_node_by_def_id(def_id);

match node {
Node::Ctor(VariantData::Tuple(..)) => Constness::Const,
Node::Ctor(VariantData::Tuple(..)) => Constness::Const { always: false },
Node::ForeignItem(item) if let ForeignItemKind::Fn(..) = item.kind => {
// Foreign functions cannot be evaluated at compile-time.
Constness::NotConst
}
Node::Expr(e) if let ExprKind::Closure(c) = e.kind => {
if let Constness::Const = c.constness && tcx.hir_body_const_context(tcx.local_parent(def_id)).is_none() {
if let Constness::Const { .. } = c.constness && tcx.hir_body_const_context(tcx.local_parent(def_id)).is_none() {
tcx.dcx().span_err(tcx.def_span(def_id), "cannot use `const` closures outside of const contexts");
return Constness::NotConst;
}
Expand All @@ -37,7 +37,7 @@ fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Constness {
..
}) => {
match sig.header.constness {
Constness::Const => Constness::Const,
Constness::Const{always} => Constness::Const {always},
// inherent impl could be const
Constness::NotConst => tcx.constness(tcx.local_parent(def_id)),
}
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,10 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
rustc_no_writable,
"`#[rustc_no_writable]` stops the compiler from considering mutable reference arguments of this function as implicitly writable"
),
rustc_attr!(
rustc_comptime,
"the `#[rustc_comptime]` attribute is just used to avoid adding syntax for `comptime fn`"
),

// ==========================================================================
// Internal attributes, Testing:
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_hir/src/attrs/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -967,6 +967,9 @@ pub enum AttributeKind {
/// Represents `#[compiler_builtins]`.
CompilerBuiltins,

/// Represents `#[rustc_comptime]`
Comptime(Span),

/// Represents `#[const_continue]`.
ConstContinue(Span),

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/attrs/encode_cross_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ impl AttributeKind {
Cold => No,
CollapseDebugInfo(..) => Yes,
CompilerBuiltins => No,
Comptime(..) => No,
ConstContinue(..) => No,
Coroutine => No,
Coverage(..) => No,
Expand Down
27 changes: 15 additions & 12 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2355,10 +2355,12 @@ pub enum ConstContext {
/// - Array length expressions
/// - Enum discriminants
/// - Const generics
///
/// For the most part, other contexts are treated just like a regular `const`, so they are
/// lumped into the same category.
Const { inline: bool },
Const {
/// For backwards compatibility `const` items allow
/// calls to `const fn` to get promoted.
/// We forbid that in comptime fns and inline consts.
allow_const_fn_promotion: bool,
},
}

impl ConstContext {
Expand Down Expand Up @@ -4522,17 +4524,22 @@ impl fmt::Display for Safety {
}

#[derive(Copy, Clone, PartialEq, Eq, Debug, Encodable, Decodable, StableHash)]
#[derive(Default)]
pub enum Constness {
Comment thread
oli-obk marked this conversation as resolved.
#[default]
Const,
Const { always: bool },
NotConst,
}

impl Default for Constness {
fn default() -> Self {
Self::Const { always: false }
}
}

impl fmt::Display for Constness {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match *self {
Self::Const => "const",
Self::Const { always: true } => "comptime",
Self::Const { always: false } => "const",
Self::NotConst => "non-const",
})
}
Expand Down Expand Up @@ -4586,10 +4593,6 @@ impl FnHeader {
matches!(self.asyncness, IsAsync::Async(_))
}

pub fn is_const(&self) -> bool {
matches!(self.constness, Constness::Const)
}

pub fn is_unsafe(&self) -> bool {
self.safety().is_unsafe()
}
Expand Down
7 changes: 4 additions & 3 deletions compiler/rustc_hir_pretty/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,7 @@ impl<'a> State<'a> {

match of_trait {
None => {
if let hir::Constness::Const = constness {
if let hir::Constness::Const { always: false } = constness {
self.word_nbsp("const");
}
impl_generics(self)
Expand All @@ -733,7 +733,7 @@ impl<'a> State<'a> {

impl_generics(self);

if let hir::Constness::Const = constness {
if let hir::Constness::Const { always: false } = constness {
self.word_nbsp("const");
}

Expand Down Expand Up @@ -2630,7 +2630,8 @@ impl<'a> State<'a> {
fn print_constness(&mut self, s: hir::Constness) {
match s {
hir::Constness::NotConst => {}
hir::Constness::Const => self.word_nbsp("const"),
hir::Constness::Const { always: false } => self.word_nbsp("const"),
hir::Constness::Const { always: true } => { /* printed as an attribute */ }
}
}

Expand Down
13 changes: 12 additions & 1 deletion compiler/rustc_hir_typeck/src/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
callee_did: DefId,
callee_args: GenericArgsRef<'tcx>,
) {
let const_context = self.tcx.hir_body_const_context(self.body_id);

if let hir::Constness::Const { always: true } = self.tcx.constness(callee_did) {
match const_context {
Some(hir::ConstContext::Const { .. } | hir::ConstContext::Static(_)) => {}
Some(hir::ConstContext::ConstFn) | None => {
self.dcx().span_err(span, "comptime fns can only be called at compile time");
}
}
}

// FIXME(const_trait_impl): We should be enforcing these effects unconditionally.
// This can be done as soon as we convert the standard library back to
// using const traits, since if we were to enforce these conditions now,
Expand All @@ -932,7 +943,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return;
}

let host = match self.tcx.hir_body_const_context(self.body_id) {
let host = match const_context {
Some(hir::ConstContext::Const { .. } | hir::ConstContext::Static(_)) => {
ty::BoundConstness::Const
}
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_metadata/src/rmeta/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,9 @@ defaulted_enum! {

defaulted_enum! {
hir::Constness {
( Const )
( Const { always: false } )
( NotConst )
( Const { always: true } )
}
}

Expand Down
12 changes: 10 additions & 2 deletions compiler/rustc_middle/src/hir/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,15 +313,23 @@ impl<'tcx> TyCtxt<'tcx> {
pub fn hir_body_const_context(self, local_def_id: LocalDefId) -> Option<ConstContext> {
let def_id = local_def_id.into();
let ccx = match self.hir_body_owner_kind(def_id) {
BodyOwnerKind::Const { inline } => ConstContext::Const { inline },
BodyOwnerKind::Const { inline } => {
ConstContext::Const { allow_const_fn_promotion: !inline }
}
BodyOwnerKind::Static(mutability) => ConstContext::Static(mutability),

BodyOwnerKind::Fn if self.is_constructor(def_id) => return None,
// Const closures use their parent's const context
BodyOwnerKind::Closure if self.is_const_fn(def_id) => {
return self.hir_body_const_context(self.local_parent(local_def_id));
}
BodyOwnerKind::Fn if self.is_const_fn(def_id) => ConstContext::ConstFn,
BodyOwnerKind::Fn if self.is_const_fn(def_id) => {
if matches!(self.constness(def_id), rustc_hir::Constness::Const { always: true }) {
ConstContext::Const { allow_const_fn_promotion: false }
} else {
ConstContext::ConstFn
}
}
BodyOwnerKind::Fn | BodyOwnerKind::Closure | BodyOwnerKind::GlobalAsm => return None,
};

Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_middle/src/ty/adt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,8 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef<TyCtxt<'tcx>> for AdtDef<'tcx> {

fn destructor(self, tcx: TyCtxt<'tcx>) -> Option<AdtDestructorKind> {
Some(match tcx.constness(self.destructor(tcx)?.did) {
hir::Constness::Const => AdtDestructorKind::Const,
hir::Constness::Const { always: true } => todo!("FIXME(comptime)"),
hir::Constness::Const { always: false } => AdtDestructorKind::Const,
hir::Constness::NotConst => AdtDestructorKind::NotConst,
})
}
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2645,7 +2645,10 @@ impl<'tcx> TyCtxt<'tcx> {
/// Whether the trait impl is marked const. This does not consider stability or feature gates.
pub fn is_const_trait_impl(self, def_id: DefId) -> bool {
self.def_kind(def_id) == DefKind::Impl { of_trait: true }
&& self.impl_trait_header(def_id).constness == hir::Constness::Const
&& matches!(
self.impl_trait_header(def_id).constness,
hir::Constness::Const { always: false }
)
}

pub fn is_sdylib_interface_build(self) -> bool {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/context/impl_interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> {

fn closure_is_const(self, def_id: DefId) -> bool {
debug_assert_matches!(self.def_kind(def_id), DefKind::Closure);
self.constness(def_id) == hir::Constness::Const
matches!(self.constness(def_id), hir::Constness::Const { always: false })
}

fn alias_has_const_conditions(self, def_id: DefId) -> bool {
Expand Down
Loading
Loading