Be better at enforcing that const_conditions is only called on const items

This commit is contained in:
Michael Goulet 2024-10-23 16:53:59 +00:00
parent 25c9253379
commit 0f5a47d088
12 changed files with 126 additions and 120 deletions

View File

@ -89,7 +89,11 @@ pub(crate) fn push_const_bound(
host: ty::HostPolarity,
span: Span,
) {
self.clauses.push((bound_trait_ref.to_host_effect_clause(tcx, host), span));
if tcx.is_const_trait(bound_trait_ref.def_id()) {
self.clauses.push((bound_trait_ref.to_host_effect_clause(tcx, host), span));
} else {
tcx.dcx().span_delayed_bug(span, "tried to lower {host:?} bound for non-const trait");
}
}
pub(crate) fn clauses(

View File

@ -206,8 +206,8 @@ fn compare_method_predicate_entailment<'tcx>(
);
// FIXME(effects): This should be replaced with a more dedicated method.
let check_const_if_const = tcx.constness(impl_def_id) == hir::Constness::Const;
if check_const_if_const {
let is_conditionally_const = tcx.is_conditionally_const(impl_def_id);
if is_conditionally_const {
// Augment the hybrid param-env with the const conditions
// of the impl header and the trait method.
hybrid_preds.extend(
@ -255,7 +255,7 @@ fn compare_method_predicate_entailment<'tcx>(
// This registers the `~const` bounds of the impl method, which we will prove
// using the hybrid param-env that we earlier augmented with the const conditions
// from the impl header and trait method declaration.
if check_const_if_const {
if is_conditionally_const {
for (const_condition, span) in
tcx.const_conditions(impl_m.def_id).instantiate_own_identity()
{
@ -1904,9 +1904,8 @@ fn compare_type_predicate_entailment<'tcx>(
let trait_ty_predicates = tcx.predicates_of(trait_ty.def_id);
let impl_ty_own_bounds = impl_ty_predicates.instantiate_own_identity();
let impl_ty_own_const_conditions =
tcx.const_conditions(impl_ty.def_id).instantiate_own_identity();
if impl_ty_own_bounds.len() == 0 && impl_ty_own_const_conditions.len() == 0 {
// If there are no bounds, then there are no const conditions, so no need to check that here.
if impl_ty_own_bounds.len() == 0 {
// Nothing to check.
return Ok(());
}
@ -1931,8 +1930,8 @@ fn compare_type_predicate_entailment<'tcx>(
let impl_ty_span = tcx.def_span(impl_ty_def_id);
let normalize_cause = ObligationCause::misc(impl_ty_span, impl_ty_def_id);
let check_const_if_const = tcx.constness(impl_def_id) == hir::Constness::Const;
if check_const_if_const {
let is_conditionally_const = tcx.is_conditionally_const(impl_ty.def_id);
if is_conditionally_const {
// Augment the hybrid param-env with the const conditions
// of the impl header and the trait assoc type.
hybrid_preds.extend(
@ -1968,8 +1967,10 @@ fn compare_type_predicate_entailment<'tcx>(
ocx.register_obligation(traits::Obligation::new(tcx, cause, param_env, predicate));
}
if check_const_if_const {
if is_conditionally_const {
// Validate the const conditions of the impl associated type.
let impl_ty_own_const_conditions =
tcx.const_conditions(impl_ty.def_id).instantiate_own_identity();
for (const_condition, span) in impl_ty_own_const_conditions {
let normalize_cause = traits::ObligationCause::misc(span, impl_ty_def_id);
let const_condition = ocx.normalize(&normalize_cause, param_env, const_condition);
@ -2081,7 +2082,7 @@ pub(super) fn check_type_bounds<'tcx>(
.collect();
// Only in a const implementation do we need to check that the `~const` item bounds hold.
if tcx.constness(container_id) == hir::Constness::Const {
if tcx.is_conditionally_const(impl_ty_def_id) {
obligations.extend(
tcx.implied_const_bounds(trait_ty.def_id)
.iter_instantiated_copied(tcx, rebased_args)

View File

@ -1372,7 +1372,7 @@ fn check_impl<'tcx>(
}
// Ensure that the `~const` where clauses of the trait hold for the impl.
if tcx.constness(item.owner_id.def_id) == hir::Constness::Const {
if tcx.is_conditionally_const(item.owner_id.def_id) {
for (bound, _) in
tcx.const_conditions(trait_ref.def_id).instantiate(tcx, trait_ref.args)
{

View File

@ -888,48 +888,8 @@ pub(super) fn const_conditions<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
) -> ty::ConstConditions<'tcx> {
// This logic is spaghetti, and should be cleaned up. The current methods that are
// defined to deal with constness are very unintuitive.
if tcx.is_const_fn_raw(def_id.to_def_id()) {
// Ok, const fn or method in const trait.
} else {
match tcx.def_kind(def_id) {
DefKind::Trait => {
if !tcx.is_const_trait(def_id.to_def_id()) {
return Default::default();
}
}
DefKind::Impl { .. } => {
// FIXME(effects): Should be using a dedicated function to
// test if this is a const trait impl.
if tcx.constness(def_id) != hir::Constness::Const {
return Default::default();
}
}
DefKind::AssocTy | DefKind::AssocFn => {
let parent_def_id = tcx.local_parent(def_id).to_def_id();
match tcx.associated_item(def_id).container {
ty::AssocItemContainer::TraitContainer => {
if !tcx.is_const_trait(parent_def_id) {
return Default::default();
}
}
ty::AssocItemContainer::ImplContainer => {
// FIXME(effects): Should be using a dedicated function to
// test if this is a const trait impl.
if tcx.constness(parent_def_id) != hir::Constness::Const {
return Default::default();
}
}
}
}
DefKind::Closure | DefKind::OpaqueTy => {
// Closures and RPITs will eventually have const conditions
// for `~const` bounds.
return Default::default();
}
_ => return Default::default(),
}
if !tcx.is_conditionally_const(def_id) {
bug!("const_conditions invoked for item that is not conditionally const: {def_id:?}");
}
let (generics, trait_def_id_and_supertraits, has_parent) = match tcx.hir_node_by_def_id(def_id)
@ -940,7 +900,7 @@ pub(super) fn const_conditions<'tcx>(
hir::ItemKind::Trait(_, _, generics, supertraits, _) => {
(generics, Some((item.owner_id.def_id, supertraits)), false)
}
_ => return Default::default(),
_ => bug!("const_conditions called on wrong item: {def_id:?}"),
},
// While associated types are not really const, we do allow them to have `~const`
// bounds and where clauses. `const_conditions` is responsible for gathering
@ -950,13 +910,21 @@ pub(super) fn const_conditions<'tcx>(
hir::TraitItemKind::Fn(_, _) | hir::TraitItemKind::Type(_, _) => {
(item.generics, None, true)
}
_ => return Default::default(),
_ => bug!("const_conditions called on wrong item: {def_id:?}"),
},
Node::ImplItem(item) => match item.kind {
hir::ImplItemKind::Fn(_, _) | hir::ImplItemKind::Type(_) => (item.generics, None, true),
_ => return Default::default(),
hir::ImplItemKind::Fn(_, _) | hir::ImplItemKind::Type(_) => {
(item.generics, None, tcx.is_conditionally_const(tcx.local_parent(def_id)))
}
_ => bug!("const_conditions called on wrong item: {def_id:?}"),
},
_ => return Default::default(),
Node::ForeignItem(item) => match item.kind {
hir::ForeignItemKind::Fn(_, _, generics) => (generics, None, false),
_ => bug!("const_conditions called on wrong item: {def_id:?}"),
},
// N.B. Tuple ctors are unconditionally constant.
Node::Ctor(hir::VariantData::Tuple { .. }) => return Default::default(),
_ => bug!("const_conditions called on wrong item: {def_id:?}"),
};
let icx = ItemCtxt::new(tcx, def_id);
@ -1017,12 +985,12 @@ pub(super) fn implied_const_bounds<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
) -> ty::EarlyBinder<'tcx, &'tcx [(ty::PolyTraitRef<'tcx>, Span)]> {
if !tcx.is_conditionally_const(def_id) {
bug!("const_conditions invoked for item that is not conditionally const: {def_id:?}");
}
let bounds = match tcx.hir_node_by_def_id(def_id) {
Node::Item(hir::Item { kind: hir::ItemKind::Trait(..), .. }) => {
if !tcx.is_const_trait(def_id.to_def_id()) {
return ty::EarlyBinder::bind(&[]);
}
implied_predicates_with_filter(
tcx,
def_id.to_def_id(),
@ -1030,17 +998,9 @@ pub(super) fn implied_const_bounds<'tcx>(
)
}
Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Type(..), .. }) => {
if !tcx.is_const_trait(tcx.local_parent(def_id).to_def_id()) {
return ty::EarlyBinder::bind(&[]);
}
explicit_item_bounds_with_filter(tcx, def_id, PredicateFilter::ConstIfConst)
}
Node::OpaqueTy(..) => {
// We should eventually collect the `~const` bounds on opaques.
return ty::EarlyBinder::bind(&[]);
}
_ => return ty::EarlyBinder::bind(&[]),
_ => bug!("implied_const_bounds called on wrong item: {def_id:?}"),
};
bounds.map_bound(|bounds| {

View File

@ -861,12 +861,7 @@ pub(super) fn enforce_context_effects(
// FIXME(effects): Should this be `is_const_fn_raw`? It depends on if we move
// const stability checking here too, I guess.
if self.tcx.is_const_fn(callee_did)
|| self
.tcx
.trait_of_item(callee_did)
.is_some_and(|def_id| self.tcx.is_const_trait(def_id))
{
if self.tcx.is_conditionally_const(callee_did) {
let q = self.tcx.const_conditions(callee_did);
// FIXME(effects): Use this span with a better cause code.
for (cond, _) in q.instantiate(self.tcx, callee_args) {

View File

@ -1423,7 +1423,6 @@ fn encode_def_ids(&mut self) {
let g = tcx.generics_of(def_id);
record!(self.tables.generics_of[def_id] <- g);
record!(self.tables.explicit_predicates_of[def_id] <- self.tcx.explicit_predicates_of(def_id));
record!(self.tables.const_conditions[def_id] <- self.tcx.const_conditions(def_id));
let inferred_outlives = self.tcx.inferred_outlives_of(def_id);
record_defaulted_array!(self.tables.inferred_outlives_of[def_id] <- inferred_outlives);
@ -1434,6 +1433,9 @@ fn encode_def_ids(&mut self) {
}
}
}
if tcx.is_conditionally_const(def_id) {
record!(self.tables.const_conditions[def_id] <- self.tcx.const_conditions(def_id));
}
if should_encode_type(tcx, local_id, def_kind) {
record!(self.tables.type_of[def_id] <- self.tcx.type_of(def_id));
}
@ -1457,11 +1459,13 @@ fn encode_def_ids(&mut self) {
self.tcx.explicit_super_predicates_of(def_id).skip_binder());
record_defaulted_array!(self.tables.explicit_implied_predicates_of[def_id] <-
self.tcx.explicit_implied_predicates_of(def_id).skip_binder());
record_defaulted_array!(self.tables.implied_const_bounds[def_id]
<- self.tcx.implied_const_bounds(def_id).skip_binder());
let module_children = self.tcx.module_children_local(local_id);
record_array!(self.tables.module_children_non_reexports[def_id] <-
module_children.iter().map(|child| child.res.def_id().index));
if self.tcx.is_const_trait(def_id) {
record_defaulted_array!(self.tables.implied_const_bounds[def_id]
<- self.tcx.implied_const_bounds(def_id).skip_binder());
}
}
if let DefKind::TraitAlias = def_kind {
record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id));
@ -1469,8 +1473,6 @@ fn encode_def_ids(&mut self) {
self.tcx.explicit_super_predicates_of(def_id).skip_binder());
record_defaulted_array!(self.tables.explicit_implied_predicates_of[def_id] <-
self.tcx.explicit_implied_predicates_of(def_id).skip_binder());
record_defaulted_array!(self.tables.implied_const_bounds[def_id]
<- self.tcx.implied_const_bounds(def_id).skip_binder());
}
if let DefKind::Trait | DefKind::Impl { .. } = def_kind {
let associated_item_def_ids = self.tcx.associated_item_def_ids(def_id);
@ -1653,8 +1655,10 @@ fn encode_info_for_assoc_item(&mut self, def_id: DefId) {
if let ty::AssocKind::Type = item.kind {
self.encode_explicit_item_bounds(def_id);
self.encode_explicit_item_super_predicates(def_id);
record_defaulted_array!(self.tables.implied_const_bounds[def_id]
<- self.tcx.implied_const_bounds(def_id).skip_binder());
if tcx.is_conditionally_const(def_id) {
record_defaulted_array!(self.tables.implied_const_bounds[def_id]
<- self.tcx.implied_const_bounds(def_id).skip_binder());
}
}
}
AssocItemContainer::ImplContainer => {

View File

@ -384,7 +384,7 @@ fn explicit_implied_predicates_of(
}
fn is_const_impl(self, def_id: DefId) -> bool {
self.constness(def_id) == hir::Constness::Const
self.is_conditionally_const(def_id)
}
fn const_conditions(
@ -3140,6 +3140,7 @@ pub fn is_const_fn(self, def_id: DefId) -> bool {
}
}
// FIXME(effects): Please remove this. It's a footgun.
/// Whether the trait impl is marked const. This does not consider stability or feature gates.
pub fn is_const_trait_impl_raw(self, def_id: DefId) -> bool {
let Some(local_def_id) = def_id.as_local() else { return false };

View File

@ -1999,10 +1999,75 @@ pub fn adjust_ident_and_get_scope(
pub fn is_const_fn_raw(self, def_id: DefId) -> bool {
matches!(
self.def_kind(def_id),
DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) | DefKind::Closure
DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::Closure
) && self.constness(def_id) == hir::Constness::Const
}
/// Whether this item is conditionally constant for the purposes of the
/// effects implementation.
///
/// This roughly corresponds to all const functions and other callable
/// items, along with const impls and traits, and associated types within
/// those impls and traits.
pub fn is_conditionally_const(self, def_id: impl Into<DefId>) -> bool {
let def_id: DefId = def_id.into();
match self.def_kind(def_id) {
DefKind::Impl { of_trait: true } => {
self.constness(def_id) == hir::Constness::Const
&& self.is_const_trait(
self.trait_id_of_impl(def_id)
.expect("expected trait for trait implementation"),
)
}
DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) => {
self.constness(def_id) == hir::Constness::Const
}
DefKind::Trait => self.is_const_trait(def_id),
DefKind::AssocTy | DefKind::AssocFn => {
let parent_def_id = self.parent(def_id);
match self.def_kind(parent_def_id) {
DefKind::Impl { of_trait: false } => {
self.constness(def_id) == hir::Constness::Const
}
DefKind::Impl { of_trait: true } | DefKind::Trait => {
self.is_conditionally_const(parent_def_id)
}
_ => bug!("unexpected parent item of associated item: {parent_def_id:?}"),
}
}
DefKind::Closure | DefKind::OpaqueTy => {
// Closures and RPITs will eventually have const conditions
// for `~const` bounds.
false
}
DefKind::Ctor(_, CtorKind::Const)
| DefKind::Impl { of_trait: false }
| DefKind::Mod
| DefKind::Struct
| DefKind::Union
| DefKind::Enum
| DefKind::Variant
| DefKind::TyAlias
| DefKind::ForeignTy
| DefKind::TraitAlias
| DefKind::TyParam
| DefKind::Const
| DefKind::ConstParam
| DefKind::Static { .. }
| DefKind::AssocConst
| DefKind::Macro(_)
| DefKind::ExternCrate
| DefKind::Use
| DefKind::ForeignMod
| DefKind::AnonConst
| DefKind::InlineConst
| DefKind::Field
| DefKind::LifetimeParam
| DefKind::GlobalAsm
| DefKind::SyntheticCoroutineBody => false,
}
}
#[inline]
pub fn is_const_trait(self, def_id: DefId) -> bool {
self.trait_def(def_id).constness == hir::Constness::Const

View File

@ -152,12 +152,13 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> {
// We extend the param-env of our item with the const conditions of the item,
// since we're allowed to assume `~const` bounds hold within the item itself.
predicates.extend(
tcx.const_conditions(def_id)
.instantiate_identity(tcx)
.into_iter()
.map(|(trait_ref, _)| trait_ref.to_host_effect_clause(tcx, ty::HostPolarity::Maybe)),
);
if tcx.is_conditionally_const(def_id) {
predicates.extend(
tcx.const_conditions(def_id).instantiate_identity(tcx).into_iter().map(
|(trait_ref, _)| trait_ref.to_host_effect_clause(tcx, ty::HostPolarity::Maybe),
),
);
}
let local_did = def_id.as_local();

View File

@ -30,14 +30,5 @@ LL | #[derive_const(PartialEq)]
|
= note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info)
error: `~const` can only be applied to `#[const_trait]` traits
--> $DIR/derive-const-with-params.rs:7:16
|
LL | #[derive_const(PartialEq)]
| ^^^^^^^^^
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
= note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 4 previous errors; 1 warning emitted
error: aborting due to 3 previous errors; 1 warning emitted

View File

@ -14,9 +14,7 @@ impl<T> const Foo for T {}
impl<T> const Foo for T where T: const Specialize {}
//~^ error: const `impl` for trait `Foo` which is not marked with `#[const_trait]`
//~| error: `const` can only be applied to `#[const_trait]` traits
//~| error: `const` can only be applied to `#[const_trait]` traits
//~| error: specialization impl does not specialize any associated items
//~| error: cannot specialize on trait `Specialize`
//~| ERROR cannot specialize on predicate
fn main() {}

View File

@ -42,14 +42,6 @@ error: `const` can only be applied to `#[const_trait]` traits
LL | impl<T> const Foo for T where T: const Specialize {}
| ^^^^^^^^^^
error: `const` can only be applied to `#[const_trait]` traits
--> $DIR/spec-effectvar-ice.rs:14:40
|
LL | impl<T> const Foo for T where T: const Specialize {}
| ^^^^^^^^^^
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error: specialization impl does not specialize any associated items
--> $DIR/spec-effectvar-ice.rs:14:1
|
@ -62,17 +54,11 @@ note: impl is a specialization of this impl
LL | impl<T> const Foo for T {}
| ^^^^^^^^^^^^^^^^^^^^^^^
error: cannot specialize on predicate `T: const Specialize`
--> $DIR/spec-effectvar-ice.rs:14:34
|
LL | impl<T> const Foo for T where T: const Specialize {}
| ^^^^^^^^^^^^^^^^
error: cannot specialize on trait `Specialize`
--> $DIR/spec-effectvar-ice.rs:14:34
|
LL | impl<T> const Foo for T where T: const Specialize {}
| ^^^^^^^^^^^^^^^^
error: aborting due to 8 previous errors; 1 warning emitted
error: aborting due to 6 previous errors; 1 warning emitted