Skip to content

Commit

Permalink
Rework const stability checking for traits
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Oct 28, 2024
1 parent df4ca44 commit faa1ef5
Show file tree
Hide file tree
Showing 10 changed files with 340 additions and 197 deletions.
76 changes: 19 additions & 57 deletions compiler/rustc_const_eval/src/check_consts/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -570,11 +570,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
};

let ConstCx { tcx, body, param_env, .. } = *self.ccx;
let caller = self.def_id();

let fn_ty = func.ty(body, tcx);

let (mut callee, mut fn_args) = match *fn_ty.kind() {
let (mut callee, fn_args) = match *fn_ty.kind() {
ty::FnDef(def_id, fn_args) => (def_id, fn_args),

ty::FnPtr(..) => {
Expand Down Expand Up @@ -614,57 +613,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
infcx.err_ctxt().report_fulfillment_errors(errors);
}

let mut is_trait = false;
// Attempting to call a trait method?
if let Some(trait_did) = tcx.trait_of_item(callee) {
trace!("attempting to call a trait method");

let trait_is_const = tcx.is_const_trait(trait_did);
// trait method calls are only permitted when `effects` is enabled.
// typeck ensures the conditions for calling a const trait method are met,
// so we only error if the trait isn't const. We try to resolve the trait
// into the concrete method, and uses that for const stability checks.
// FIXME(effects) we might consider moving const stability checks to typeck as well.
if tcx.features().effects() && trait_is_const {
// This skips the check below that ensures we only call `const fn`.
is_trait = true;

if let Ok(Some(instance)) =
Instance::try_resolve(tcx, param_env, callee, fn_args)
&& let InstanceKind::Item(def) = instance.def
{
// Resolve a trait method call to its concrete implementation, which may be in a
// `const` trait impl. This is only used for the const stability check below, since
// we want to look at the concrete impl's stability.
fn_args = instance.args;
callee = def;
}
} else {
// if the trait is const but the user has not enabled the feature(s),
// suggest them.
let feature = if trait_is_const {
Some(if tcx.features().const_trait_impl() {
sym::effects
} else {
sym::const_trait_impl
})
} else {
None
};
self.check_op(ops::FnCallNonConst {
caller,
callee,
args: fn_args,
span: *fn_span,
call_source,
feature,
});
// If we allowed this, we're in miri-unleashed mode, so we might
// as well skip the remaining checks.
return;
}
}

// At this point, we are calling a function, `callee`, whose `DefId` is known...

// `begin_panic` and `#[rustc_const_panic_str]` functions accept generic
Expand Down Expand Up @@ -740,15 +688,29 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
}

// Trait functions are not `const fn` so we have to skip them here.
if !tcx.is_const_fn(callee) && !is_trait {
self.check_op(ops::FnCallNonConst {
caller,
if tcx.is_const_fn(callee) {
// Allowed, modulo stability below.
} else if let Some(_) = tcx.trait_of_item(callee) {
self.check_op(ops::TraitCallNonConst {
callee,
args: fn_args,
span: *fn_span,
call_source,
feature: None,
});

// Resolve a trait method call to its concrete implementation, which
// may be in a `const` trait impl. This is only used for the const
// stability check below, since we want to look at the concrete impl's
// stability.
// FIXME(effects): Do we *actually* want to do this?
if let Ok(Some(instance)) =
Instance::try_resolve(tcx, param_env, callee, fn_args)
&& let InstanceKind::Item(def) = instance.def
{
callee = def;
}
} else {
self.check_op(ops::FnCallNonConst { callee, args: fn_args, span: *fn_span });
// If we allowed this, we're in miri-unleashed mode, so we might
// as well skip the remaining checks.
return;
Expand Down
85 changes: 68 additions & 17 deletions compiler/rustc_const_eval/src/check_consts/ops.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//! Concrete error types for all operations which may be invalid in a certain const context.

use hir::def_id::LocalDefId;
use hir::{ConstContext, LangItem};
use rustc_errors::Diag;
use rustc_errors::codes::*;
Expand Down Expand Up @@ -71,24 +70,36 @@ impl<'tcx> NonConstOp<'tcx> for FnCallIndirect {
}
}

/// A function call where the callee is not marked as `const`.
#[derive(Debug, Clone, Copy)]
pub(crate) struct FnCallNonConst<'tcx> {
pub caller: LocalDefId,
#[derive(Debug)]
pub(crate) struct TraitCallNonConst<'tcx> {
pub callee: DefId,
pub args: GenericArgsRef<'tcx>,
pub span: Span,
pub call_source: CallSource,
pub feature: Option<Symbol>,
}
impl<'tcx> NonConstOp<'tcx> for TraitCallNonConst<'tcx> {
fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status {
if ccx
.tcx
.trait_of_item(self.callee)
.is_some_and(|trait_def_id| ccx.tcx.is_const_trait(trait_def_id))
{
Status::Unstable {
gate: sym::const_trait_impl,
safe_to_expose_on_stable: false,
is_function_call: true,
}
} else {
Status::Forbidden
}
}

impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
// FIXME: make this translatable
#[allow(rustc::diagnostic_outside_of_impl)]
#[allow(rustc::untranslatable_diagnostic)]
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> Diag<'tcx> {
let FnCallNonConst { caller, callee, args, span, call_source, feature } = *self;
let ConstCx { tcx, param_env, body, .. } = *ccx;
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _span: Span) -> Diag<'tcx> {
let TraitCallNonConst { callee, args, span, call_source } = *self;
let ConstCx { tcx, param_env, .. } = *ccx;
let caller = ccx.def_id();

let diag_trait = |err, self_ty: Ty<'_>, trait_id| {
let trait_ref = TraitRef::from_method(tcx, trait_id, args);
Expand All @@ -113,6 +124,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
}
}
ty::Adt(..) => {
// FIXME(effects): Don't use select here.
let obligation =
Obligation::new(tcx, ObligationCause::dummy(), param_env, trait_ref);

Expand Down Expand Up @@ -270,9 +282,6 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
diag_trait(&mut err, self_ty, tcx.require_lang_item(LangItem::Deref, Some(span)));
err
}
_ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::ArgumentMethods) => {
ccx.dcx().create_err(errors::NonConstFmtMacroCall { span, kind: ccx.const_kind() })
}
_ => ccx.dcx().create_err(errors::NonConstFnCall {
span,
def_path_str: ccx.tcx.def_path_str_with_args(callee, args),
Expand All @@ -286,14 +295,56 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
ccx.const_kind(),
));

if let Some(feature) = feature {
if let ConstContext::Static(_) = ccx.const_kind() {
err.note(fluent_generated::const_eval_lazy_lock);
}

if tcx
.trait_of_item(self.callee)
.is_some_and(|trait_def_id| tcx.is_const_trait(trait_def_id))
{
ccx.tcx.disabled_nightly_features(
&mut err,
body.source.def_id().as_local().map(|local| ccx.tcx.local_def_id_to_hir_id(local)),
[(String::new(), feature)],
Some(ccx.tcx.local_def_id_to_hir_id(ccx.def_id())),
[(String::new(), sym::const_trait_impl)],
);
}

err
}
}

/// A function call where the callee is not marked as `const`.
#[derive(Debug, Clone, Copy)]
pub(crate) struct FnCallNonConst<'tcx> {
pub callee: DefId,
pub args: GenericArgsRef<'tcx>,
pub span: Span,
}

impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
#[allow(rustc::diagnostic_outside_of_impl)]
#[allow(rustc::untranslatable_diagnostic)]
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> Diag<'tcx> {
let FnCallNonConst { callee, args, span } = *self;
let tcx = ccx.tcx;

let mut err = if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::ArgumentMethods) {
ccx.dcx().create_err(errors::NonConstFmtMacroCall { span, kind: ccx.const_kind() })
} else {
ccx.dcx().create_err(errors::NonConstFnCall {
span,
def_path_str: ccx.tcx.def_path_str_with_args(callee, args),
kind: ccx.const_kind(),
})
};

err.note(format!(
"calls in {}s are limited to constant functions, \
tuple structs and tuple variants",
ccx.const_kind(),
));

if let ConstContext::Static(_) = ccx.const_kind() {
err.note(fluent_generated::const_eval_lazy_lock);
}
Expand Down
51 changes: 1 addition & 50 deletions tests/ui/specialization/const_trait_impl.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -40,54 +40,5 @@ LL | impl<T: ~const Default + ~const Sub> const A for T {
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error[E0015]: cannot call non-const fn `<() as A>::a` in constants
--> $DIR/const_trait_impl.rs:52:23
|
LL | const _: () = assert!(<()>::a() == 42);
| ^^^^^^^^^
|
= note: calls in constants are limited to constant functions, tuple structs and tuple variants
help: add `#![feature(effects)]` to the crate attributes to enable
|
LL + #![feature(effects)]
|

error[E0015]: cannot call non-const fn `<u8 as A>::a` in constants
--> $DIR/const_trait_impl.rs:53:23
|
LL | const _: () = assert!(<u8>::a() == 3);
| ^^^^^^^^^
|
= note: calls in constants are limited to constant functions, tuple structs and tuple variants
help: add `#![feature(effects)]` to the crate attributes to enable
|
LL + #![feature(effects)]
|

error[E0015]: cannot call non-const fn `<u16 as A>::a` in constants
--> $DIR/const_trait_impl.rs:54:23
|
LL | const _: () = assert!(<u16>::a() == 2);
| ^^^^^^^^^^
|
= note: calls in constants are limited to constant functions, tuple structs and tuple variants
help: add `#![feature(effects)]` to the crate attributes to enable
|
LL + #![feature(effects)]
|

error[E0015]: cannot call non-const fn `<T as Sup>::foo` in constant functions
--> $DIR/const_trait_impl.rs:48:9
|
LL | T::foo()
| ^^^^^^^^
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
help: add `#![feature(effects)]` to the crate attributes to enable
|
LL + #![feature(effects)]
|

error: aborting due to 10 previous errors
error: aborting due to 6 previous errors

For more information about this error, try `rustc --explain E0015`.
26 changes: 1 addition & 25 deletions tests/ui/traits/const-traits/call-const-trait-method-pass.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,6 @@ LL | const ADD_INT: Int = Int(1i32) + Int(2i32);
|
= note: calls in constants are limited to constant functions, tuple structs and tuple variants

error[E0015]: cannot call non-const fn `<i32 as Plus>::plus` in constant functions
--> $DIR/call-const-trait-method-pass.rs:11:20
|
LL | Int(self.0.plus(rhs.0))
| ^^^^^^^^^^^
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
help: add `#![feature(effects)]` to the crate attributes to enable
|
LL + #![feature(effects)]
|

error[E0015]: cannot call non-const fn `<Int as PartialEq>::eq` in constant functions
--> $DIR/call-const-trait-method-pass.rs:20:15
|
Expand All @@ -44,18 +32,6 @@ LL | !self.eq(other)
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants

error[E0015]: cannot call non-const fn `<i32 as Plus>::plus` in constant functions
--> $DIR/call-const-trait-method-pass.rs:36:7
|
LL | a.plus(b)
| ^^^^^^^
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
help: add `#![feature(effects)]` to the crate attributes to enable
|
LL + #![feature(effects)]
|

error: aborting due to 6 previous errors
error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0015`.
17 changes: 2 additions & 15 deletions tests/ui/traits/const-traits/const-drop-fail-2.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,6 @@ LL | const fn check<T: ~const Destruct>(_: T) {}
| |
| the destructor for this type cannot be evaluated in constant functions

error[E0015]: cannot call non-const fn `<T as A>::a` in constant functions
--> $DIR/const-drop-fail-2.rs:41:9
|
LL | T::a();
| ^^^^^^
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
help: add `#![feature(effects)]` to the crate attributes to enable
|
LL + #![feature(effects)]
|

error: aborting due to 5 previous errors
error: aborting due to 4 previous errors

Some errors have detailed explanations: E0015, E0493.
For more information about an error, try `rustc --explain E0015`.
For more information about this error, try `rustc --explain E0493`.
17 changes: 2 additions & 15 deletions tests/ui/traits/const-traits/const-drop.precise.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -54,19 +54,6 @@ error[E0493]: destructor of `T` cannot be evaluated at compile-time
LL | const fn a<T: ~const Destruct>(_: T) {}
| ^ the destructor for this type cannot be evaluated in constant functions

error[E0015]: cannot call non-const fn `<T as SomeTrait>::foo` in constant functions
--> $DIR/const-drop.rs:69:13
|
LL | T::foo();
| ^^^^^^^^
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
help: add `#![feature(effects)]` to the crate attributes to enable
|
LL + #![feature(effects)]
|

error: aborting due to 8 previous errors
error: aborting due to 7 previous errors

Some errors have detailed explanations: E0015, E0493.
For more information about an error, try `rustc --explain E0015`.
For more information about this error, try `rustc --explain E0493`.
17 changes: 2 additions & 15 deletions tests/ui/traits/const-traits/const-drop.stock.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,6 @@ LL | const fn a<T: ~const Destruct>(_: T) {}
| |
| the destructor for this type cannot be evaluated in constant functions

error[E0015]: cannot call non-const fn `<T as SomeTrait>::foo` in constant functions
--> $DIR/const-drop.rs:69:13
|
LL | T::foo();
| ^^^^^^^^
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
help: add `#![feature(effects)]` to the crate attributes to enable
|
LL + #![feature(effects)]
|

error: aborting due to 8 previous errors
error: aborting due to 7 previous errors

Some errors have detailed explanations: E0015, E0493.
For more information about an error, try `rustc --explain E0015`.
For more information about this error, try `rustc --explain E0493`.
Loading

0 comments on commit faa1ef5

Please sign in to comment.