Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework const stability checking for traits #132280

Closed
wants to merge 1 commit into from
Closed
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
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
Loading