Skip to content

Commit cea506b

Browse files
Auto merge of #142293 - fmease:gci-dont-eval-the-impossible, r=<try>
GCI: Don't evaluate the initializer of free const items that have trivially unsatisfied predicates
2 parents e4b9d01 + 0e9126c commit cea506b

File tree

12 files changed

+166
-187
lines changed

12 files changed

+166
-187
lines changed

compiler/rustc_hir_analysis/src/lib.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -239,12 +239,18 @@ pub fn check_crate(tcx: TyCtxt<'_>) {
239239
check::maybe_check_static_with_link_section(tcx, item_def_id);
240240
}
241241
DefKind::Const if !tcx.generics_of(item_def_id).own_requires_monomorphization() => {
242-
// FIXME(generic_const_items): Passing empty instead of identity args is fishy but
243-
// seems to be fine for now. Revisit this!
244-
let instance = ty::Instance::new_raw(item_def_id.into(), ty::GenericArgs::empty());
245-
let cid = GlobalId { instance, promoted: None };
246-
let typing_env = ty::TypingEnv::fully_monomorphized();
247-
tcx.ensure_ok().eval_to_const_value_raw(typing_env.as_query_input(cid));
242+
let predicates = tcx.predicates_of(item_def_id);
243+
let predicates = predicates.instantiate_identity(tcx).predicates;
244+
245+
if !traits::impossible_predicates(tcx, predicates) {
246+
// FIXME(generic_const_items): Passing empty instead of identity args is fishy but
247+
// seems to be fine for now. Revisit this!
248+
let instance =
249+
ty::Instance::new_raw(item_def_id.into(), ty::GenericArgs::empty());
250+
let cid = GlobalId { instance, promoted: None };
251+
let typing_env = ty::TypingEnv::fully_monomorphized();
252+
tcx.ensure_ok().eval_to_const_value_raw(typing_env.as_query_input(cid));
253+
}
248254
}
249255
_ => (),
250256
}

compiler/rustc_middle/src/mir/interpret/queries.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::ty::{self, GenericArgs, TyCtxt, TypeVisitableExt};
1313

1414
impl<'tcx> TyCtxt<'tcx> {
1515
/// Evaluates a constant without providing any generic parameters. This is useful to evaluate consts
16-
/// that can't take any generic arguments like const items or enum discriminants. If a
16+
/// that can't take any generic arguments like enum discriminants. If a
1717
/// generic parameter is used within the constant `ErrorHandled::TooGeneric` will be returned.
1818
#[instrument(skip(self), level = "debug")]
1919
pub fn const_eval_poly(self, def_id: DefId) -> EvalToConstValueResult<'tcx> {
@@ -29,7 +29,7 @@ impl<'tcx> TyCtxt<'tcx> {
2929
}
3030

3131
/// Evaluates a constant without providing any generic parameters. This is useful to evaluate consts
32-
/// that can't take any generic arguments like const items or enum discriminants. If a
32+
/// that can't take any generic arguments like enum discriminants. If a
3333
/// generic parameter is used within the constant `ErrorHandled::TooGeneric` will be returned.
3434
#[instrument(skip(self), level = "debug")]
3535
pub fn const_eval_poly_to_alloc(self, def_id: DefId) -> EvalToAllocationRawResult<'tcx> {

compiler/rustc_monomorphize/src/collector.rs

Lines changed: 66 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1434,72 +1434,62 @@ struct RootCollector<'a, 'tcx> {
14341434
entry_fn: Option<(DefId, EntryFnType)>,
14351435
}
14361436

1437-
impl<'v> RootCollector<'_, 'v> {
1437+
impl<'tcx> RootCollector<'_, 'tcx> {
14381438
fn process_item(&mut self, id: hir::ItemId) {
1439-
match self.tcx.def_kind(id.owner_id) {
1439+
let tcx = self.tcx;
1440+
let def_id = id.owner_id.to_def_id();
1441+
1442+
match tcx.def_kind(def_id) {
14401443
DefKind::Enum | DefKind::Struct | DefKind::Union => {
1441-
if self.strategy == MonoItemCollectionStrategy::Eager
1442-
&& !self.tcx.generics_of(id.owner_id).requires_monomorphization(self.tcx)
1443-
{
1444-
debug!("RootCollector: ADT drop-glue for `{id:?}`",);
1445-
let id_args =
1446-
ty::GenericArgs::for_item(self.tcx, id.owner_id.to_def_id(), |param, _| {
1447-
match param.kind {
1448-
GenericParamDefKind::Lifetime => {
1449-
self.tcx.lifetimes.re_erased.into()
1450-
}
1451-
GenericParamDefKind::Type { .. }
1452-
| GenericParamDefKind::Const { .. } => {
1453-
unreachable!(
1454-
"`own_requires_monomorphization` check means that \
1455-
we should have no type/const params"
1456-
)
1457-
}
1458-
}
1459-
});
1460-
1461-
// This type is impossible to instantiate, so we should not try to
1462-
// generate a `drop_in_place` instance for it.
1463-
if self.tcx.instantiate_and_check_impossible_predicates((
1464-
id.owner_id.to_def_id(),
1465-
id_args,
1466-
)) {
1467-
return;
1468-
}
1444+
if self.strategy != MonoItemCollectionStrategy::Eager {
1445+
return;
1446+
}
14691447

1470-
let ty =
1471-
self.tcx.type_of(id.owner_id.to_def_id()).instantiate(self.tcx, id_args);
1472-
assert!(!ty.has_non_region_param());
1473-
visit_drop_use(self.tcx, ty, true, DUMMY_SP, self.output);
1448+
if tcx.generics_of(def_id).requires_monomorphization(tcx) {
1449+
return;
1450+
}
1451+
let args = ty::GenericArgs::for_item(tcx, def_id, |param, _| {
1452+
expect_and_erase_regions(tcx, param)
1453+
});
1454+
if tcx.instantiate_and_check_impossible_predicates((def_id, args)) {
1455+
return;
14741456
}
1457+
1458+
let ty = tcx.type_of(def_id).instantiate(tcx, args);
1459+
debug_assert!(!ty.has_non_region_param());
1460+
1461+
debug!("RootCollector: ADT drop-glue for `{id:?}`");
1462+
visit_drop_use(tcx, ty, true, DUMMY_SP, self.output);
14751463
}
14761464
DefKind::GlobalAsm => {
1477-
debug!(
1478-
"RootCollector: ItemKind::GlobalAsm({})",
1479-
self.tcx.def_path_str(id.owner_id)
1480-
);
1465+
debug!("RootCollector: ItemKind::GlobalAsm({})", tcx.def_path_str(def_id));
14811466
self.output.push(dummy_spanned(MonoItem::GlobalAsm(id)));
14821467
}
14831468
DefKind::Static { .. } => {
1484-
let def_id = id.owner_id.to_def_id();
1485-
debug!("RootCollector: ItemKind::Static({})", self.tcx.def_path_str(def_id));
1469+
debug!("RootCollector: ItemKind::Static({})", tcx.def_path_str(def_id));
14861470
self.output.push(dummy_spanned(MonoItem::Static(def_id)));
14871471
}
14881472
DefKind::Const => {
1489-
// Const items only generate mono items if they are actually used somewhere.
1490-
// Just declaring them is insufficient.
1473+
if tcx.generics_of(def_id).requires_monomorphization(tcx) {
1474+
return;
1475+
}
14911476

1492-
// But even just declaring them must collect the items they refer to
1493-
// unless their generics require monomorphization.
1494-
if !self.tcx.generics_of(id.owner_id).own_requires_monomorphization()
1495-
&& let Ok(val) = self.tcx.const_eval_poly(id.owner_id.to_def_id())
1496-
{
1497-
collect_const_value(self.tcx, val, self.output);
1477+
let args = ty::GenericArgs::for_item(tcx, def_id, |param, _| {
1478+
expect_and_erase_regions(tcx, param)
1479+
});
1480+
if tcx.instantiate_and_check_impossible_predicates((def_id, args)) {
1481+
return;
14981482
}
1483+
1484+
let Ok(val) = tcx.const_eval_poly(def_id) else { return };
1485+
1486+
// Const items only generate mono items if they are actually used somewhere.
1487+
// Just declaring them is insufficient.
1488+
collect_const_value(tcx, val, self.output);
14991489
}
15001490
DefKind::Impl { .. } => {
15011491
if self.strategy == MonoItemCollectionStrategy::Eager {
1502-
create_mono_items_for_default_impls(self.tcx, id, self.output);
1492+
create_mono_items_for_default_impls(tcx, id, self.output);
15031493
}
15041494
}
15051495
DefKind::Fn => {
@@ -1622,35 +1612,23 @@ fn create_mono_items_for_default_impls<'tcx>(
16221612
item: hir::ItemId,
16231613
output: &mut MonoItems<'tcx>,
16241614
) {
1625-
let Some(impl_) = tcx.impl_trait_header(item.owner_id) else {
1626-
return;
1627-
};
1628-
1615+
let impl_def_id = item.owner_id.to_def_id();
1616+
let Some(impl_) = tcx.impl_trait_header(impl_def_id) else { return };
16291617
if matches!(impl_.polarity, ty::ImplPolarity::Negative) {
16301618
return;
16311619
}
16321620

1633-
if tcx.generics_of(item.owner_id).own_requires_monomorphization() {
1634-
return;
1635-
}
1636-
16371621
// Lifetimes never affect trait selection, so we are allowed to eagerly
16381622
// instantiate an instance of an impl method if the impl (and method,
16391623
// which we check below) is only parameterized over lifetime. In that case,
16401624
// we use the ReErased, which has no lifetime information associated with
16411625
// it, to validate whether or not the impl is legal to instantiate at all.
1642-
let only_region_params = |param: &ty::GenericParamDef, _: &_| match param.kind {
1643-
GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(),
1644-
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
1645-
unreachable!(
1646-
"`own_requires_monomorphization` check means that \
1647-
we should have no type/const params"
1648-
)
1649-
}
1650-
};
1651-
let impl_args = GenericArgs::for_item(tcx, item.owner_id.to_def_id(), only_region_params);
1652-
let trait_ref = impl_.trait_ref.instantiate(tcx, impl_args);
1653-
1626+
if tcx.generics_of(impl_def_id).requires_monomorphization(tcx) {
1627+
return;
1628+
}
1629+
let impl_args = ty::GenericArgs::for_item(tcx, impl_def_id, |param, _| {
1630+
expect_and_erase_regions(tcx, param)
1631+
});
16541632
// Unlike 'lazy' monomorphization that begins by collecting items transitively
16551633
// called by `main` or other global items, when eagerly monomorphizing impl
16561634
// items, we never actually check that the predicates of this impl are satisfied
@@ -1660,13 +1638,15 @@ fn create_mono_items_for_default_impls<'tcx>(
16601638
// consider higher-ranked predicates such as `for<'a> &'a mut [u8]: Copy` to
16611639
// be trivially false. We must now check that the impl has no impossible-to-satisfy
16621640
// predicates.
1663-
if tcx.instantiate_and_check_impossible_predicates((item.owner_id.to_def_id(), impl_args)) {
1641+
if tcx.instantiate_and_check_impossible_predicates((impl_def_id, impl_args)) {
16641642
return;
16651643
}
16661644

1645+
let trait_ref = impl_.trait_ref.instantiate(tcx, impl_args);
1646+
16671647
let typing_env = ty::TypingEnv::fully_monomorphized();
16681648
let trait_ref = tcx.normalize_erasing_regions(typing_env, trait_ref);
1669-
let overridden_methods = tcx.impl_item_implementor_ids(item.owner_id);
1649+
let overridden_methods = tcx.impl_item_implementor_ids(impl_def_id);
16701650
for method in tcx.provided_trait_methods(trait_ref.def_id) {
16711651
if overridden_methods.contains_key(&method.def_id) {
16721652
continue;
@@ -1679,7 +1659,9 @@ fn create_mono_items_for_default_impls<'tcx>(
16791659
// As mentioned above, the method is legal to eagerly instantiate if it
16801660
// only has lifetime generic parameters. This is validated by calling
16811661
// `own_requires_monomorphization` on both the impl and method.
1682-
let args = trait_ref.args.extend_to(tcx, method.def_id, only_region_params);
1662+
let args = trait_ref
1663+
.args
1664+
.extend_to(tcx, method.def_id, |param, _| expect_and_erase_regions(tcx, param));
16831665
let instance = ty::Instance::expect_resolve(tcx, typing_env, method.def_id, args, DUMMY_SP);
16841666

16851667
let mono_item = create_fn_mono_item(tcx, instance, DUMMY_SP);
@@ -1689,6 +1671,18 @@ fn create_mono_items_for_default_impls<'tcx>(
16891671
}
16901672
}
16911673

1674+
fn expect_and_erase_regions<'tcx>(
1675+
tcx: TyCtxt<'tcx>,
1676+
param: &ty::GenericParamDef,
1677+
) -> ty::GenericArg<'tcx> {
1678+
match param.kind {
1679+
GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(),
1680+
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
1681+
bug!("unexpected non-region param")
1682+
}
1683+
}
1684+
}
1685+
16921686
//=-----------------------------------------------------------------------------
16931687
// Top-level entry point, tying it all together
16941688
//=-----------------------------------------------------------------------------

compiler/rustc_passes/src/reachable.rs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use rustc_middle::query::Providers;
3737
use rustc_middle::ty::{self, ExistentialTraitRef, TyCtxt};
3838
use rustc_privacy::DefIdVisitor;
3939
use rustc_session::config::CrateType;
40+
use rustc_trait_selection::traits;
4041
use tracing::debug;
4142

4243
/// Determines whether this item is recursive for reachability. See `is_recursively_reachable_local`
@@ -205,19 +206,34 @@ impl<'tcx> ReachableContext<'tcx> {
205206
}
206207

207208
hir::ItemKind::Const(_, _, _, init) => {
208-
// Only things actually ending up in the final constant value are reachable
209-
// for codegen. Everything else is only needed during const-eval, so even if
210-
// const-eval happens in a downstream crate, all they need is
211-
// `mir_for_ctfe`.
209+
if self.tcx.generics_of(item.owner_id).own_requires_monomorphization() {
210+
// In this case, we don't want to evaluate the const initializer.
211+
// In lieu of that, we have to consider everything mentioned in it
212+
// as reachable, since it *may* end up in the final value.
213+
self.visit_nested_body(init);
214+
return;
215+
}
216+
217+
let predicates = self.tcx.predicates_of(item.owner_id);
218+
let predicates = predicates.instantiate_identity(self.tcx).predicates;
219+
if traits::impossible_predicates(self.tcx, predicates) {
220+
// The constant is impossible to reference.
221+
// Therefore nothing can be reachable via it.
222+
return;
223+
}
224+
212225
match self.tcx.const_eval_poly_to_alloc(item.owner_id.def_id.into()) {
213226
Ok(alloc) => {
227+
// Only things actually ending up in the final constant value are
228+
// reachable for codegen. Everything else is only needed during
229+
// const-eval, so even if const-eval happens in a downstream crate,
230+
// all they need is `mir_for_ctfe`.
214231
let alloc = self.tcx.global_alloc(alloc.alloc_id).unwrap_memory();
215232
self.propagate_from_alloc(alloc);
216233
}
217-
// We can't figure out which value the constant will evaluate to. In
218-
// lieu of that, we have to consider everything mentioned in the const
219-
// initializer reachable, since it *may* end up in the final value.
220-
Err(ErrorHandled::TooGeneric(_)) => self.visit_nested_body(init),
234+
// We've checked at the start that there aren't any non-lifetime params
235+
// in scope. The const initializer can't possibly be too generic.
236+
Err(ErrorHandled::TooGeneric(_)) => bug!(),
221237
// If there was an error evaluating the const, nothing can be reachable
222238
// via it, and anyway compilation will fail.
223239
Err(ErrorHandled::Reported(..)) => {}

compiler/rustc_trait_selection/src/traits/mod.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -719,8 +719,13 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>(
719719
/// returns true, then either normalize encountered an error or one of the predicates did not
720720
/// hold. Used when creating vtables to check for unsatisfiable methods. This should not be
721721
/// used during analysis.
722+
#[instrument(level = "debug", skip(tcx))]
722723
pub fn impossible_predicates<'tcx>(tcx: TyCtxt<'tcx>, predicates: Vec<ty::Clause<'tcx>>) -> bool {
723-
debug!("impossible_predicates(predicates={:?})", predicates);
724+
debug_assert!(!predicates.has_non_region_param());
725+
if predicates.is_empty() {
726+
return false;
727+
}
728+
724729
let (infcx, param_env) = tcx
725730
.infer_ctxt()
726731
.with_next_trait_solver(true)
@@ -747,26 +752,23 @@ pub fn impossible_predicates<'tcx>(tcx: TyCtxt<'tcx>, predicates: Vec<ty::Clause
747752
false
748753
}
749754

755+
#[instrument(level = "debug", skip(tcx), ret)]
750756
fn instantiate_and_check_impossible_predicates<'tcx>(
751757
tcx: TyCtxt<'tcx>,
752-
key: (DefId, GenericArgsRef<'tcx>),
758+
(def_id, args): (DefId, GenericArgsRef<'tcx>),
753759
) -> bool {
754-
debug!("instantiate_and_check_impossible_predicates(key={:?})", key);
755-
756-
let mut predicates = tcx.predicates_of(key.0).instantiate(tcx, key.1).predicates;
760+
let mut predicates = tcx.predicates_of(def_id).instantiate(tcx, args).predicates;
757761

758762
// Specifically check trait fulfillment to avoid an error when trying to resolve
759763
// associated items.
760-
if let Some(trait_def_id) = tcx.trait_of_item(key.0) {
761-
let trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, key.1);
764+
if let Some(trait_def_id) = tcx.trait_of_item(def_id) {
765+
let trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, args);
762766
predicates.push(trait_ref.upcast(tcx));
763767
}
764768

765769
predicates.retain(|predicate| !predicate.has_param());
766-
let result = impossible_predicates(tcx, predicates);
767770

768-
debug!("instantiate_and_check_impossible_predicates(key={:?}) = {:?}", key, result);
769-
result
771+
impossible_predicates(tcx, predicates)
770772
}
771773

772774
/// Checks whether a trait's associated item is impossible to reference on a given impl.

tests/codegen/dont_codegen_private_const_fn_only_used_in_const_eval.rs

Lines changed: 0 additions & 27 deletions
This file was deleted.

0 commit comments

Comments
 (0)