diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:11:38 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:12:43 +0000 |
commit | cf94bdc0742c13e2a0cac864c478b8626b266e1b (patch) | |
tree | 044670aa50cc5e2b4229aa0b6b3df6676730c0a6 /compiler/rustc_const_eval/src/transform/check_consts | |
parent | Adding debian version 1.65.0+dfsg1-2. (diff) | |
download | rustc-cf94bdc0742c13e2a0cac864c478b8626b266e1b.tar.xz rustc-cf94bdc0742c13e2a0cac864c478b8626b266e1b.zip |
Merging upstream version 1.66.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_const_eval/src/transform/check_consts')
5 files changed, 109 insertions, 74 deletions
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 7e15858c8..22a61774e 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -13,8 +13,11 @@ use rustc_middle::ty::{self, adjustment::PointerCast, Instance, InstanceDef, Ty, use rustc_middle::ty::{Binder, TraitPredicate, TraitRef, TypeVisitable}; use rustc_mir_dataflow::{self, Analysis}; use rustc_span::{sym, Span, Symbol}; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt; -use rustc_trait_selection::traits::SelectionContext; +use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; +use rustc_trait_selection::traits::{ + self, ObligationCauseCode, SelectionContext, TraitEngine, TraitEngineExt, +}; use std::mem; use std::ops::Deref; @@ -550,7 +553,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { unimplemented!() } - Rvalue::Cast(CastKind::Misc, _, _) => {} + Rvalue::Cast(_, _, _) => {} Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) => {} Rvalue::ShallowInitBox(_, _) => {} @@ -656,6 +659,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { ProjectionElem::ConstantIndex { .. } | ProjectionElem::Downcast(..) + | ProjectionElem::OpaqueCast(..) | ProjectionElem::Subslice { .. } | ProjectionElem::Field(..) | ProjectionElem::Index(_) => {} @@ -733,10 +737,49 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { let obligation = Obligation::new(ObligationCause::dummy(), param_env, poly_trait_pred); - let implsrc = tcx.infer_ctxt().enter(|infcx| { + let implsrc = { + let infcx = tcx.infer_ctxt().build(); let mut selcx = SelectionContext::new(&infcx); selcx.select(&obligation) - }); + }; + + // do a well-formedness check on the trait method being called. This is because typeck only does a + // "non-const" check. This is required for correctness here. + { + let infcx = tcx.infer_ctxt().build(); + let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx); + let predicates = tcx.predicates_of(callee).instantiate(tcx, substs); + let hir_id = tcx + .hir() + .local_def_id_to_hir_id(self.body.source.def_id().expect_local()); + let cause = || { + ObligationCause::new( + terminator.source_info.span, + hir_id, + ObligationCauseCode::ItemObligation(callee), + ) + }; + let normalized = infcx.partially_normalize_associated_types_in( + cause(), + param_env, + predicates, + ); + + for p in normalized.obligations { + fulfill_cx.register_predicate_obligation(&infcx, p); + } + for obligation in traits::predicates_for_generics( + |_, _| cause(), + self.param_env, + normalized.value, + ) { + fulfill_cx.register_predicate_obligation(&infcx, obligation); + } + let errors = fulfill_cx.select_all_or_error(&infcx); + if !errors.is_empty() { + infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + } + } match implsrc { Ok(Some(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => { @@ -794,16 +837,15 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // improve diagnostics by showing what failed. Our requirements are stricter this time // as we are going to error again anyways. - tcx.infer_ctxt().enter(|infcx| { - if let Err(e) = implsrc { - infcx.report_selection_error( - obligation.clone(), - &obligation, - &e, - false, - ); - } - }); + let infcx = tcx.infer_ctxt().build(); + if let Err(e) = implsrc { + infcx.err_ctxt().report_selection_error( + obligation.clone(), + &obligation, + &e, + false, + ); + } self.check_op(ops::FnCallNonConst { caller, @@ -867,8 +909,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { return; } - let is_intrinsic = tcx.is_intrinsic(callee); - if !tcx.is_const_fn_raw(callee) { if !tcx.is_const_default_method(callee) { // To get to here we must have already found a const impl for the @@ -928,7 +968,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // We do not use `const` modifiers for intrinsic "functions", as intrinsics are // `extern` functions, and these have no way to get marked `const`. So instead we // use `rustc_const_(un)stable` attributes to mean that the intrinsic is `const` - if self.ccx.is_const_stable_const_fn() || is_intrinsic { + if self.ccx.is_const_stable_const_fn() || tcx.is_intrinsic(callee) { self.check_op(ops::FnCallUnstable(callee, None)); return; } @@ -968,7 +1008,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { if needs_non_const_drop { self.check_op_spanned( - ops::LiveDrop { dropped_at: Some(terminator.source_info.span) }, + ops::LiveDrop { + dropped_at: Some(terminator.source_info.span), + dropped_ty: ty_of_dropped_place, + }, err_span, ); } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index 5fb4bf638..b28d70194 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -156,10 +156,9 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { }), ); - let implsrc = tcx.infer_ctxt().enter(|infcx| { - let mut selcx = SelectionContext::new(&infcx); - selcx.select(&obligation) - }); + let infcx = tcx.infer_ctxt().build(); + let mut selcx = SelectionContext::new(&infcx); + let implsrc = selcx.select(&obligation); if let Ok(Some(ImplSource::UserDefined(data))) = implsrc { let span = tcx.def_span(data.impl_def_id); @@ -422,10 +421,11 @@ impl<'tcx> NonConstOp<'tcx> for InlineAsm { } #[derive(Debug)] -pub struct LiveDrop { +pub struct LiveDrop<'tcx> { pub dropped_at: Option<Span>, + pub dropped_ty: Ty<'tcx>, } -impl<'tcx> NonConstOp<'tcx> for LiveDrop { +impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> { fn build_error( &self, ccx: &ConstCx<'_, 'tcx>, @@ -435,9 +435,13 @@ impl<'tcx> NonConstOp<'tcx> for LiveDrop { ccx.tcx.sess, span, E0493, - "destructors cannot be evaluated at compile-time" + "destructor of `{}` cannot be evaluated at compile-time", + self.dropped_ty, + ); + err.span_label( + span, + format!("the destructor for this type cannot be evaluated in {}s", ccx.const_kind()), ); - err.span_label(span, format!("{}s cannot evaluate destructors", ccx.const_kind())); if let Some(span) = self.dropped_at { err.span_label(span, "value is dropped here"); } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs index 4e210f663..d4570c598 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs @@ -1,6 +1,6 @@ use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::{self, BasicBlock, Location}; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{Ty, TyCtxt}; use rustc_span::{symbol::sym, Span}; use super::check::Qualifs; @@ -58,9 +58,9 @@ impl<'mir, 'tcx> std::ops::Deref for CheckLiveDrops<'mir, 'tcx> { } } -impl CheckLiveDrops<'_, '_> { - fn check_live_drop(&self, span: Span) { - ops::LiveDrop { dropped_at: None }.build_error(self.ccx, span).emit(); +impl<'tcx> CheckLiveDrops<'_, 'tcx> { + fn check_live_drop(&self, span: Span, dropped_ty: Ty<'tcx>) { + ops::LiveDrop { dropped_at: None, dropped_ty }.build_error(self.ccx, span).emit(); } } @@ -90,7 +90,7 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> { } if dropped_place.is_indirect() { - self.check_live_drop(terminator.source_info.span); + self.check_live_drop(terminator.source_info.span, dropped_ty); return; } @@ -101,7 +101,7 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> { if self.qualifs.needs_non_const_drop(self.ccx, dropped_place.local, location) { // Use the span where the dropped local was declared for the error. let span = self.body.local_decls[dropped_place.local].source_info.span; - self.check_live_drop(span); + self.check_live_drop(span, dropped_ty); } } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index 6c73ef5a8..335992342 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -5,9 +5,9 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir::LangItem; use rustc_infer::infer::TyCtxtInferExt; +use rustc_middle::mir; use rustc_middle::mir::*; use rustc_middle::ty::{self, subst::SubstsRef, AdtDef, Ty}; -use rustc_span::DUMMY_SP; use rustc_trait_selection::traits::{ self, ImplSource, Obligation, ObligationCause, SelectionContext, }; @@ -91,7 +91,7 @@ impl Qualif for HasMutInterior { } fn in_any_value_of_ty<'tcx>(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool { - !ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env) + !ty.is_freeze(cx.tcx, cx.param_env) } fn in_adt_inherently<'tcx>( @@ -167,30 +167,28 @@ impl Qualif for NeedsNonConstDrop { }), ); - cx.tcx.infer_ctxt().enter(|infcx| { - let mut selcx = SelectionContext::new(&infcx); - let Some(impl_src) = selcx.select(&obligation).ok().flatten() else { - // If we couldn't select a const destruct candidate, then it's bad - return true; - }; - - if !matches!( - impl_src, - ImplSource::ConstDestruct(_) - | ImplSource::Param(_, ty::BoundConstness::ConstIfConst) - ) { - // If our const destruct candidate is not ConstDestruct or implied by the param env, - // then it's bad - return true; - } + let infcx = cx.tcx.infer_ctxt().build(); + let mut selcx = SelectionContext::new(&infcx); + let Some(impl_src) = selcx.select(&obligation).ok().flatten() else { + // If we couldn't select a const destruct candidate, then it's bad + return true; + }; + + if !matches!( + impl_src, + ImplSource::ConstDestruct(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst) + ) { + // If our const destruct candidate is not ConstDestruct or implied by the param env, + // then it's bad + return true; + } - if impl_src.borrow_nested_obligations().is_empty() { - return false; - } + if impl_src.borrow_nested_obligations().is_empty() { + return false; + } - // If we had any errors, then it's bad - !traits::fully_solve_obligations(&infcx, impl_src.nested_obligations()).is_empty() - }) + // If we had any errors, then it's bad + !traits::fully_solve_obligations(&infcx, impl_src.nested_obligations()).is_empty() } fn in_adt_inherently<'tcx>( @@ -308,6 +306,7 @@ where ProjectionElem::Deref | ProjectionElem::Field(_, _) + | ProjectionElem::OpaqueCast(_) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } | ProjectionElem::Downcast(_, _) @@ -349,17 +348,13 @@ where // FIXME(valtrees): check whether const qualifs should behave the same // way for type and mir constants. let uneval = match constant.literal { - ConstantKind::Ty(ct) if matches!(ct.kind(), ty::ConstKind::Unevaluated(_)) => { - let ty::ConstKind::Unevaluated(uv) = ct.kind() else { unreachable!() }; - - Some(uv.expand()) - } - ConstantKind::Ty(_) => None, + ConstantKind::Ty(ct) if matches!(ct.kind(), ty::ConstKind::Param(_)) => None, + ConstantKind::Ty(c) => bug!("expected ConstKind::Param here, found {:?}", c), ConstantKind::Unevaluated(uv, _) => Some(uv), ConstantKind::Val(..) => None, }; - if let Some(ty::Unevaluated { def, substs: _, promoted }) = uneval { + if let Some(mir::UnevaluatedConst { def, substs: _, promoted }) = uneval { // Use qualifs of the type for the promoted. Promoteds in MIR body should be possible // only for `NeedsNonConstDrop` with precise drop checking. This is the only const // check performed after the promotion. Verify that with an assertion. @@ -367,11 +362,8 @@ where // Don't peek inside trait associated constants. if promoted.is_none() && cx.tcx.trait_of_item(def.did).is_none() { - let qualifs = if let Some((did, param_did)) = def.as_const_arg() { - cx.tcx.at(constant.span).mir_const_qualif_const_arg((did, param_did)) - } else { - cx.tcx.at(constant.span).mir_const_qualif(def.did) - }; + assert_eq!(def.const_param_did, None, "expected associated const: {def:?}"); + let qualifs = cx.tcx.at(constant.span).mir_const_qualif(def.did); if !Q::in_qualifs(&qualifs) { return false; diff --git a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs index 60c1e4950..805e6096b 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs @@ -8,7 +8,6 @@ use rustc_middle::mir::{self, BasicBlock, Local, Location, Statement, StatementK use rustc_mir_dataflow::fmt::DebugWithContext; use rustc_mir_dataflow::JoinSemiLattice; use rustc_mir_dataflow::{Analysis, AnalysisDomain, CallReturnPlaces}; -use rustc_span::DUMMY_SP; use std::fmt; use std::marker::PhantomData; @@ -120,10 +119,7 @@ where /// /// [rust-lang/unsafe-code-guidelines#134]: https://github.com/rust-lang/unsafe-code-guidelines/issues/134 fn shared_borrow_allows_mutation(&self, place: mir::Place<'tcx>) -> bool { - !place - .ty(self.ccx.body, self.ccx.tcx) - .ty - .is_freeze(self.ccx.tcx.at(DUMMY_SP), self.ccx.param_env) + !place.ty(self.ccx.body, self.ccx.tcx).ty.is_freeze(self.ccx.tcx, self.ccx.param_env) } } |