Double-check conditional constness in MIR

To prevent any conditional constness from leaking through during MIR lowering
This commit is contained in:
Michael Goulet 2024-10-26 05:23:13 +00:00
parent 145f9cf95d
commit e319838e8d
3 changed files with 72 additions and 29 deletions

View File

@ -11,7 +11,6 @@ use rustc_hir::def_id::DefId;
use rustc_hir::{self as hir, LangItem}; use rustc_hir::{self as hir, LangItem};
use rustc_index::bit_set::BitSet; use rustc_index::bit_set::BitSet;
use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::ObligationCause;
use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::*; use rustc_middle::mir::*;
use rustc_middle::span_bug; use rustc_middle::span_bug;
@ -20,9 +19,11 @@ use rustc_middle::ty::{self, Instance, InstanceKind, Ty, TypeVisitableExt};
use rustc_mir_dataflow::Analysis; use rustc_mir_dataflow::Analysis;
use rustc_mir_dataflow::impls::MaybeStorageLive; use rustc_mir_dataflow::impls::MaybeStorageLive;
use rustc_mir_dataflow::storage::always_storage_live_locals; use rustc_mir_dataflow::storage::always_storage_live_locals;
use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use rustc_span::{Span, Symbol, sym};
use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt}; use rustc_trait_selection::traits::{
Obligation, ObligationCause, ObligationCauseCode, ObligationCtxt,
};
use tracing::{debug, instrument, trace}; use tracing::{debug, instrument, trace};
use super::ops::{self, NonConstOp, Status}; use super::ops::{self, NonConstOp, Status};
@ -360,6 +361,60 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
// end of evaluation. // end of evaluation.
!is_transient !is_transient
} }
fn revalidate_conditional_constness(
&self,
callee: DefId,
callee_args: ty::GenericArgsRef<'tcx>,
call_source: CallSource,
call_span: Span,
) {
let tcx = self.tcx;
if !tcx.is_conditionally_const(callee) {
return;
}
let infcx = tcx.infer_ctxt().build(self.body.typing_mode(tcx));
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
let const_conditions = tcx.const_conditions(callee).instantiate(tcx, callee_args);
let body_id = self.body.source.def_id().expect_local();
let host_polarity = match self.const_kind() {
hir::ConstContext::ConstFn => ty::BoundConstness::Maybe,
hir::ConstContext::Static(_) | hir::ConstContext::Const { .. } => {
ty::BoundConstness::Const
}
};
let const_conditions = ocx.normalize(
&ObligationCause::misc(call_span, body_id),
self.param_env,
const_conditions,
);
ocx.register_obligations(const_conditions.into_iter().map(|(trait_ref, span)| {
Obligation::new(
tcx,
ObligationCause::new(
call_span,
body_id,
ObligationCauseCode::WhereClause(callee, span),
),
self.param_env,
trait_ref.to_host_effect_clause(tcx, host_polarity),
)
}));
let errors = ocx.select_all_or_error();
if !errors.is_empty() {
// FIXME(effects): Soon this should be unconditionally delaying a bug.
if matches!(call_source, CallSource::Normal) && tcx.features().effects() {
tcx.dcx()
.span_delayed_bug(call_span, "this should have reported a ~const error in HIR");
} else {
infcx.err_ctxt().report_fulfillment_errors(errors);
}
}
}
} }
impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
@ -584,31 +639,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
} }
}; };
// Check that all trait bounds that are marked as `~const` can be satisfied. self.revalidate_conditional_constness(
// callee,
// Typeck only does a "non-const" check since it operates on HIR and cannot distinguish fn_args,
// which path expressions are getting called on and which path expressions are only used call_source,
// as function pointers. This is required for correctness.
let infcx = tcx.infer_ctxt().build(body.typing_mode(tcx));
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
let predicates = tcx.predicates_of(callee).instantiate(tcx, fn_args);
let cause = ObligationCause::new(
terminator.source_info.span, terminator.source_info.span,
self.body.source.def_id().expect_local(),
ObligationCauseCode::WhereClause(callee, DUMMY_SP),
); );
let normalized_predicates = ocx.normalize(&cause, param_env, predicates);
ocx.register_obligations(traits::predicates_for_generics(
|_, _| cause.clone(),
self.param_env,
normalized_predicates,
));
let errors = ocx.select_all_or_error();
if !errors.is_empty() {
infcx.err_ctxt().report_fulfillment_errors(errors);
}
let mut is_trait = false; let mut is_trait = false;
// Attempting to call a trait method? // Attempting to call a trait method?

View File

@ -18,7 +18,7 @@ const fn const_context() {
#[cfg(any(stocknc, gatednc))] #[cfg(any(stocknc, gatednc))]
NonConst.func(); NonConst.func();
//[stocknc]~^ ERROR: cannot call //[stocknc]~^ ERROR: cannot call
//[gatednc]~^^ ERROR: the trait bound //[stocknc,gatednc]~^^ ERROR: the trait bound
Const.func(); Const.func();
//[stock]~^ ERROR: cannot call //[stock]~^ ERROR: cannot call
//[stocknc]~^^ ERROR: cannot call //[stocknc]~^^ ERROR: cannot call

View File

@ -1,3 +1,9 @@
error[E0277]: the trait bound `cross_crate::NonConst: ~const cross_crate::MyTrait` is not satisfied
--> $DIR/cross-crate.rs:19:5
|
LL | NonConst.func();
| ^^^^^^^^^^^^^^^
error[E0015]: cannot call non-const fn `<cross_crate::NonConst as cross_crate::MyTrait>::func` in constant functions error[E0015]: cannot call non-const fn `<cross_crate::NonConst as cross_crate::MyTrait>::func` in constant functions
--> $DIR/cross-crate.rs:19:14 --> $DIR/cross-crate.rs:19:14
| |
@ -22,6 +28,7 @@ help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
LL + #![feature(const_trait_impl)] LL + #![feature(const_trait_impl)]
| |
error: aborting due to 2 previous errors error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0015`. Some errors have detailed explanations: E0015, E0277.
For more information about an error, try `rustc --explain E0015`.