Auto merge of #131985 - compiler-errors:const-pred, r=fee1-dead

Represent trait constness as a distinct predicate

cc `@rust-lang/project-const-traits`
r? `@ghost` for now

Also mirrored everything that is written below on this hackmd here: https://hackmd.io/`@compiler-errors/r12zoixg1l`

# Tl;dr:

* This PR removes the bulk of the old effect desugaring.
* This PR reimplements most of the effect desugaring as a new predicate and set of a couple queries. I believe it majorly simplifies the implementation and allows us to move forward more easily on its implementation.

I'm putting this up both as a request for comments and a vibe-check, but also as a legitimate implementation that I'd like to see land (though no rush of course on that last part).

## Background

### Early days

Once upon a time, we represented trait constness in the param-env and in `TraitPredicate`. This was very difficult to implement correctly; it had bugs and was also incomplete; I don't think this was anyone's fault though, it was just the limit of experimental knowledge we had at that point.

Dealing with `~const` within predicates themselves meant dealing with constness all throughout the trait solver. This was difficult to keep track of, and afaict was not handled well with all the corners of candidate assembly.

Specifically, we had to (in various places) remap constness according to the param-env constness:

574b64a97f/compiler/rustc_trait_selection/src/traits/select/mod.rs (L1498)

This was annoying and manual and also error prone.

### Beginning of the effects desugaring

Later on, #113210 reimplemented a new desugaring for const traits via a `<const HOST: bool>` predicate. This essentially "reified" the const checking and separated it from any of the remapping or separate tracking in param-envs. For example, if I was in a const-if-const environment, but I wanted to call a trait that was non-const, this reification would turn the constness mismatch into a simple *type* mismatch of the effect parameter.

While this was a monumental step towards straightening out const trait checking in the trait system, it had its own issues, since that meant that the constness of a trait (or any item within it, like an associated type) was *early-bound*. This essentially meant that `<T as Trait>::Assoc` was *distinct* from `<T as ~const Trait>::Assoc`, which was bad.

### Associated-type bound based effects desugaring

After this, #120639 implemented a new effects desugaring. This used an associated type to more clearly represent the fact that the constness is not an input parameter of a trait, but a property that could be computed of a impl. The write-up linked in that PR explains it better than I could.

However, I feel like it really reached the limits of what can comfortably be expressed in terms of associated type and trait calculus. Also, `<const HOST: bool>` remains a synthetic const parameter, which is observable in nested items like RPITs and closures, and comes with tons of its own hacks in the astconv and middle layer.

For example, there are pieces of unintuitive code that are needed to represent semantics like elaboration, and eventually will be needed to make error reporting intuitive, and hopefully in the future assist us in implementing built-in traits (eventually we'll want something like `~const Fn` trait bounds!).

elaboration hack: 8069f8d17a/compiler/rustc_type_ir/src/elaborate.rs (L133-L195)

trait bound remapping hack for diagnostics: 8069f8d17a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs (L2370-L2413)

I want to be clear that I don't think this is a issue of implementation quality or anything like that; I think it's simply a very clear sign that we're using types and traits in a way that they're not fundamentally supposed to be used, especially given that constness deserves to be represented as a first-class concept.

### What now?

This PR implements a new desugaring for const traits. Specifically, it introduces a `HostEffect` predicate to represent the obligation an impl is const, rather than using associated type bounds and the compat trait that exists for effects today.

### `HostEffect` predicate

A `HostEffect` clause has two parts -- the `TraitRef` we're trying to prove, and a `HostPolarity::{Maybe, Const}`.

`HostPolarity::Const` corresponds to `T: const Trait` bounds, which must *always* be proven as const, and which can be written in any context. These are lowered directly into the predicates of an item, since they're not "context-specific".

On the other hand, `HostPolarity::Maybe` corresponds to `T: ~const Trait` bounds which must only exist in a conditionally-const context like a method in a `#[const_trait]`, or a `const fn` free function. We do not lower these immediately into the predicates of an item; instead, we collect them into a new query called the **`const_conditions`**. These are the set of trait refs that we need to prove have const implementations for an item to be const.

Notably, they're represented as bare (poly) trait refs because they are meant to be paired back together with a `HostPolarity` when they're being registered in typeck (see next section).

For example, given:

```rust
const fn foo<T: ~const A + const B>() {}
```

`foo`'s const conditions would contain `T: A`, but not `T: B`. On the flip side, foo's predicates (`predicates_of`) query would contain `HostEffect(T: B, HostPolarity::Const)` but not `HostEffect(T: A, HostPolarity::Maybe)` since we don't need to prove that predicate in a non-const environment (and it's not even the right predicate to prove in an unconditionally const environment).

### Type checking const bodies

When type checking bodies in HIR, when we encounter a call expression, we additionally register the callee item's const conditions with the `HostPolarity` from the body we're typechecking (`Const` for unconditionally const things like `const`/`static` items, and `Maybe` for conditionally const things like const fns; and we don't register `HostPolarity` predicates for non-const bodies).

When type-checking a conditionally const body, we augment its param-env with `HostEffect(..., Maybe)` predicates.

### Checking that const impls are WF

We extend the logic in `compare_method_predicate_entailment` to also check the const-conditions of the impl method, to make sure that we error for:

```rust
#[const_trait] Bar {}
#[const_trait] trait Foo {
    fn method<T: Bar>();
}

impl Foo for () {
    fn method<T: ~const Bar>() {} // stronger assumption!
}
```

We also extend the WF check for impls to register the const conditions of the trait that is being implemented. This is to make sure we error for:

```rust
#[const_trait] trait Bar {}
#[const_trait] trait Foo<T> where T: ~const Bar {}

impl<T> const Foo<T> for () {}
//~^ `T: ~const Bar` is missing!
```

### Proving a `HostEffect` predicate

We have several ways of proving a `HostEffect` predicate:

1. Matching a `HostEffect` predicate from the param-env
2. From an impl - we do impl selection very similar to confirming a trait goal, except we filter for only const impls, and we additionally register the impl's const conditions (i.e. the impl's `~const` where clauses).

Later I expect that we will add more built-in implementations for things like `Fn`.

## What next?

After this PR, I'd like to split out the work more so it can proceed in parallel and probably amongst others that are not me.

* Register `HostEffect` goal for places in HIR typeck that correspond to call terminators, like autoderef.
* Make traits in libstd const again.
    * Probably need to impl host effect preds in old solver.
* Implement built-in `HostEffect` rules for traits like `Fn`.
* Rip out const checking from MIR altogether.

## So what?

This ends up being super convenient basically everywhere in the compiler. Due to the design of the new trait solver, we end up having an almost parallel structure to the existing trait and projection predicates for assembling `HostEffect` predicates; adding new candidates and especially new built-in implementations is now basically trivial, and it's quite straightforward to understand the confirmation logic for these predicates.

Same with diagnostics reporting; since we have predicates which represent the obligation to prove an impl is const, we can simplify and make these diagnostics richer without having to write a ton of logic to intercept and rewrite the existing `Compat` trait errors.

Finally, it gives us a much more straightforward path for supporting the const effect on the old trait solver. I'm personally quite passionate about getting const trait support into the hands of users without having to wait until the new solver lands[^1], so I think after this PR lands we can begin to gauge how difficult it would be to implement constness in the old trait solver too. This PR will not do this yet.

[^1]: Though this is not a prerequisite or by any means the only justification for this PR.
This commit is contained in:
bors 2024-10-24 17:33:42 +00:00
commit 1d4a7670d4
221 changed files with 2363 additions and 2748 deletions

View File

@ -57,7 +57,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
owner: NodeId,
f: impl FnOnce(&mut LoweringContext<'_, 'hir>) -> hir::OwnerNode<'hir>,
) {
let mut lctx = LoweringContext::new(self.tcx, self.resolver, self.ast_index);
let mut lctx = LoweringContext::new(self.tcx, self.resolver);
lctx.with_hir_id_owner(owner, |lctx| f(lctx));
for (def_id, info) in lctx.children {
@ -193,8 +193,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
ItemKind::Const(box ast::ConstItem { generics, ty, expr, .. }) => {
let (generics, (ty, body_id)) = self.lower_generics(
generics,
Const::No,
false,
id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
@ -225,16 +223,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
);
let itctx = ImplTraitContext::Universal;
let (generics, decl) =
this.lower_generics(generics, header.constness, false, id, itctx, |this| {
this.lower_fn_decl(
decl,
id,
*fn_sig_span,
FnDeclKind::Fn,
coroutine_kind,
)
});
let (generics, decl) = this.lower_generics(generics, id, itctx, |this| {
this.lower_fn_decl(decl, id, *fn_sig_span, FnDeclKind::Fn, coroutine_kind)
});
let sig = hir::FnSig {
decl,
header: this.lower_fn_header(*header, hir::Safety::Safe),
@ -269,8 +260,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
add_ty_alias_where_clause(&mut generics, *where_clauses, true);
let (generics, ty) = self.lower_generics(
&generics,
Const::No,
false,
id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| match ty {
@ -294,8 +283,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
ItemKind::Enum(enum_definition, generics) => {
let (generics, variants) = self.lower_generics(
generics,
Const::No,
false,
id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
@ -309,8 +296,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
ItemKind::Struct(struct_def, generics) => {
let (generics, struct_def) = self.lower_generics(
generics,
Const::No,
false,
id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| this.lower_variant_data(hir_id, struct_def),
@ -320,8 +305,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
ItemKind::Union(vdata, generics) => {
let (generics, vdata) = self.lower_generics(
generics,
Const::No,
false,
id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| this.lower_variant_data(hir_id, vdata),
@ -353,7 +336,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
// parent lifetime.
let itctx = ImplTraitContext::Universal;
let (generics, (trait_ref, lowered_ty)) =
self.lower_generics(ast_generics, Const::No, false, id, itctx, |this| {
self.lower_generics(ast_generics, id, itctx, |this| {
let modifiers = TraitBoundModifiers {
constness: BoundConstness::Never,
asyncness: BoundAsyncness::Normal,
@ -405,8 +388,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
ItemKind::Trait(box Trait { is_auto, safety, generics, bounds, items }) => {
let (generics, (safety, items, bounds)) = self.lower_generics(
generics,
Const::No,
false,
id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
@ -426,8 +407,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
ItemKind::TraitAlias(generics, bounds) => {
let (generics, bounds) = self.lower_generics(
generics,
Const::No,
false,
id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
@ -604,50 +583,23 @@ impl<'hir> LoweringContext<'_, 'hir> {
ctxt: AssocCtxt,
parent_hir: &'hir hir::OwnerInfo<'hir>,
) -> hir::OwnerNode<'hir> {
// Evaluate with the lifetimes in `params` in-scope.
// This is used to track which lifetimes have already been defined,
// and which need to be replicated when lowering an async fn.
let parent_item = parent_hir.node().expect_item();
let constness = match parent_item.kind {
match parent_item.kind {
hir::ItemKind::Impl(impl_) => {
self.is_in_trait_impl = impl_.of_trait.is_some();
// N.B. the impl should always lower to methods that have `const host: bool` params if the trait
// is const. It doesn't matter whether the `impl` itself is const. Disallowing const fn from
// calling non-const impls are done through associated types.
if let Some(def_id) = impl_.of_trait.and_then(|tr| tr.trait_def_id()) {
if let Some(local_def) = def_id.as_local() {
match &self.ast_index[local_def] {
AstOwner::Item(ast::Item { attrs, .. }) => attrs
.iter()
.find(|attr| attr.has_name(sym::const_trait))
.map_or(Const::No, |attr| Const::Yes(attr.span)),
_ => Const::No,
}
} else if self.tcx.is_const_trait(def_id) {
// FIXME(effects) span
Const::Yes(self.tcx.def_ident_span(def_id).unwrap())
} else {
Const::No
}
} else {
Const::No
}
}
hir::ItemKind::Trait(_, _, _, _, _) => parent_hir
.attrs
.get(parent_item.hir_id().local_id)
.iter()
.find(|attr| attr.has_name(sym::const_trait))
.map_or(Const::No, |attr| Const::Yes(attr.span)),
hir::ItemKind::Trait(_, _, _, _, _) => {}
kind => {
span_bug!(item.span, "assoc item has unexpected kind of parent: {}", kind.descr())
}
};
}
// Evaluate with the lifetimes in `params` in-scope.
// This is used to track which lifetimes have already been defined,
// and which need to be replicated when lowering an async fn.
match ctxt {
AssocCtxt::Trait => hir::OwnerNode::TraitItem(self.lower_trait_item(item, constness)),
AssocCtxt::Impl => hir::OwnerNode::ImplItem(self.lower_impl_item(item, constness)),
AssocCtxt::Trait => hir::OwnerNode::TraitItem(self.lower_trait_item(item)),
AssocCtxt::Impl => hir::OwnerNode::ImplItem(self.lower_impl_item(item)),
}
}
@ -663,7 +615,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let fdec = &sig.decl;
let itctx = ImplTraitContext::Universal;
let (generics, (decl, fn_args)) =
self.lower_generics(generics, Const::No, false, i.id, itctx, |this| {
self.lower_generics(generics, i.id, itctx, |this| {
(
// Disallow `impl Trait` in foreign items.
this.lower_fn_decl(
@ -775,11 +727,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
}
fn lower_trait_item(
&mut self,
i: &AssocItem,
trait_constness: Const,
) -> &'hir hir::TraitItem<'hir> {
fn lower_trait_item(&mut self, i: &AssocItem) -> &'hir hir::TraitItem<'hir> {
let hir_id = self.lower_node_id(i.id);
self.lower_attrs(hir_id, &i.attrs);
let trait_item_def_id = hir_id.expect_owner();
@ -788,8 +736,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => {
let (generics, kind) = self.lower_generics(
generics,
Const::No,
false,
i.id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
@ -810,7 +756,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
i.id,
FnDeclKind::Trait,
sig.header.coroutine_kind,
trait_constness,
);
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false)
}
@ -829,7 +774,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
i.id,
FnDeclKind::Trait,
sig.header.coroutine_kind,
trait_constness,
);
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true)
}
@ -838,8 +782,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
add_ty_alias_where_clause(&mut generics, *where_clauses, false);
let (generics, kind) = self.lower_generics(
&generics,
Const::No,
false,
i.id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
@ -912,11 +854,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.expr(span, hir::ExprKind::Err(guar))
}
fn lower_impl_item(
&mut self,
i: &AssocItem,
constness_of_trait: Const,
) -> &'hir hir::ImplItem<'hir> {
fn lower_impl_item(&mut self, i: &AssocItem) -> &'hir hir::ImplItem<'hir> {
// Since `default impl` is not yet implemented, this is always true in impls.
let has_value = true;
let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value);
@ -926,8 +864,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
let (generics, kind) = match &i.kind {
AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => self.lower_generics(
generics,
Const::No,
false,
i.id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
@ -953,7 +889,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
i.id,
if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent },
sig.header.coroutine_kind,
constness_of_trait,
);
(generics, hir::ImplItemKind::Fn(sig, body_id))
@ -963,8 +898,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
add_ty_alias_where_clause(&mut generics, *where_clauses, false);
self.lower_generics(
&generics,
Const::No,
false,
i.id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| match ty {
@ -1370,18 +1303,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
id: NodeId,
kind: FnDeclKind,
coroutine_kind: Option<CoroutineKind>,
parent_constness: Const,
) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) {
let header = self.lower_fn_header(sig.header, hir::Safety::Safe);
// Don't pass along the user-provided constness of trait associated functions; we don't want to
// synthesize a host effect param for them. We reject `const` on them during AST validation.
let constness =
if kind == FnDeclKind::Inherent { sig.header.constness } else { parent_constness };
let itctx = ImplTraitContext::Universal;
let (generics, decl) =
self.lower_generics(generics, constness, kind == FnDeclKind::Impl, id, itctx, |this| {
this.lower_fn_decl(&sig.decl, id, sig.span, kind, coroutine_kind)
});
let (generics, decl) = self.lower_generics(generics, id, itctx, |this| {
this.lower_fn_decl(&sig.decl, id, sig.span, kind, coroutine_kind)
});
(generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) })
}
@ -1460,8 +1387,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn lower_generics<T>(
&mut self,
generics: &Generics,
constness: Const,
force_append_constness: bool,
parent_node_id: NodeId,
itctx: ImplTraitContext,
f: impl FnOnce(&mut Self) -> T,
@ -1524,30 +1449,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
}
// Desugar `~const` bound in generics into an additional `const host: bool` param
// if the effects feature is enabled. This needs to be done before we lower where
// clauses since where clauses need to bind to the DefId of the host param
let host_param_parts = if let Const::Yes(span) = constness
// if this comes from implementing a `const` trait, we must force constness to be appended
// to the impl item, no matter whether effects is enabled.
&& (self.tcx.features().effects() || force_append_constness)
{
let span = self.lower_span(span);
let param_node_id = self.next_node_id();
let hir_id = self.next_id();
let def_id = self.create_def(
self.local_def_id(parent_node_id),
param_node_id,
sym::host,
DefKind::ConstParam,
span,
);
self.host_param_id = Some(def_id);
Some((span, hir_id, def_id))
} else {
None
};
let mut predicates: SmallVec<[hir::WherePredicate<'hir>; 4]> = SmallVec::new();
predicates.extend(generics.params.iter().filter_map(|param| {
self.lower_generic_bound_predicate(
@ -1595,74 +1496,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
let impl_trait_bounds = std::mem::take(&mut self.impl_trait_bounds);
predicates.extend(impl_trait_bounds.into_iter());
if let Some((span, hir_id, def_id)) = host_param_parts {
let const_node_id = self.next_node_id();
let anon_const_did =
self.create_def(def_id, const_node_id, kw::Empty, DefKind::AnonConst, span);
let const_id = self.next_id();
let const_expr_id = self.next_id();
let bool_id = self.next_id();
self.children.push((def_id, hir::MaybeOwner::NonOwner(hir_id)));
self.children.push((anon_const_did, hir::MaybeOwner::NonOwner(const_id)));
let const_body = self.lower_body(|this| {
(&[], hir::Expr {
hir_id: const_expr_id,
kind: hir::ExprKind::Lit(
this.arena.alloc(hir::Lit { node: LitKind::Bool(true), span }),
),
span,
})
});
let default_ac = self.arena.alloc(hir::AnonConst {
def_id: anon_const_did,
hir_id: const_id,
body: const_body,
span,
});
let default_ct = self.arena.alloc(hir::ConstArg {
hir_id: self.next_id(),
kind: hir::ConstArgKind::Anon(default_ac),
is_desugared_from_effects: false,
});
let param = hir::GenericParam {
def_id,
hir_id,
name: hir::ParamName::Plain(Ident { name: sym::host, span }),
span,
kind: hir::GenericParamKind::Const {
ty: self.arena.alloc(self.ty(
span,
hir::TyKind::Path(hir::QPath::Resolved(
None,
self.arena.alloc(hir::Path {
res: Res::PrimTy(hir::PrimTy::Bool),
span,
segments: self.arena.alloc_from_iter([hir::PathSegment {
ident: Ident { name: sym::bool, span },
hir_id: bool_id,
res: Res::PrimTy(hir::PrimTy::Bool),
args: None,
infer_args: false,
}]),
}),
)),
)),
default: Some(default_ct),
is_host_effect: true,
synthetic: true,
},
colon_span: None,
pure_wrt_drop: false,
source: hir::GenericParamSource::Generics,
};
params.push(param);
}
let lowered_generics = self.arena.alloc(hir::Generics {
params: self.arena.alloc_from_iter(params),
predicates: self.arena.alloc_from_iter(predicates),

View File

@ -154,17 +154,10 @@ struct LoweringContext<'a, 'hir> {
/// defined on the TAIT, so we have type Foo<'a1> = ... and we establish a mapping in this
/// field from the original parameter 'a to the new parameter 'a1.
generics_def_id_map: Vec<LocalDefIdMap<LocalDefId>>,
host_param_id: Option<LocalDefId>,
ast_index: &'a IndexSlice<LocalDefId, AstOwner<'a>>,
}
impl<'a, 'hir> LoweringContext<'a, 'hir> {
fn new(
tcx: TyCtxt<'hir>,
resolver: &'a mut ResolverAstLowering,
ast_index: &'a IndexSlice<LocalDefId, AstOwner<'a>>,
) -> Self {
fn new(tcx: TyCtxt<'hir>, resolver: &'a mut ResolverAstLowering) -> Self {
Self {
// Pseudo-globals.
tcx,
@ -204,8 +197,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// interact with `gen`/`async gen` blocks
allow_async_iterator: [sym::gen_future, sym::async_iterator].into(),
generics_def_id_map: Default::default(),
host_param_id: None,
ast_index,
}
}
@ -2054,11 +2045,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
param: &GenericParam,
source: hir::GenericParamSource,
) -> hir::GenericParam<'hir> {
let (name, kind) = self.lower_generic_param_kind(
param,
source,
attr::contains_name(&param.attrs, sym::rustc_runtime),
);
let (name, kind) = self.lower_generic_param_kind(param, source);
let hir_id = self.lower_node_id(param.id);
self.lower_attrs(hir_id, &param.attrs);
@ -2078,7 +2065,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
&mut self,
param: &GenericParam,
source: hir::GenericParamSource,
is_host_effect: bool,
) -> (hir::ParamName, hir::GenericParamKind<'hir>) {
match &param.kind {
GenericParamKind::Lifetime => {
@ -2144,7 +2130,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
(
hir::ParamName::Plain(self.lower_ident(param.ident)),
hir::GenericParamKind::Const { ty, default, is_host_effect, synthetic: false },
hir::GenericParamKind::Const { ty, default, synthetic: false },
)
}
}

View File

@ -838,10 +838,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
rustc_const_panic_str, Normal, template!(Word), WarnFollowing,
EncodeCrossCrate::Yes, INTERNAL_UNSTABLE
),
rustc_attr!(
rustc_runtime, Normal, template!(Word), WarnFollowing,
EncodeCrossCrate::No, INTERNAL_UNSTABLE
),
// ==========================================================================
// Internal attributes, Layout related:

View File

@ -580,7 +580,6 @@ pub enum GenericParamKind<'hir> {
ty: &'hir Ty<'hir>,
/// Optional default value for the const generic param
default: Option<&'hir ConstArg<'hir>>,
is_host_effect: bool,
synthetic: bool,
},
}

View File

@ -935,7 +935,7 @@ pub fn walk_generic_param<'v, V: Visitor<'v>>(
match param.kind {
GenericParamKind::Lifetime { .. } => {}
GenericParamKind::Type { ref default, .. } => visit_opt!(visitor, visit_ty, default),
GenericParamKind::Const { ref ty, ref default, is_host_effect: _, synthetic: _ } => {
GenericParamKind::Const { ref ty, ref default, synthetic: _ } => {
try_visit!(visitor.visit_ty(ty));
if let Some(ref default) = default {
try_visit!(visitor.visit_const_param_default(param.hir_id, default));

View File

@ -415,14 +415,6 @@ language_item_table! {
String, sym::String, string, Target::Struct, GenericRequirement::None;
CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None;
EffectsRuntime, sym::EffectsRuntime, effects_runtime, Target::Struct, GenericRequirement::None;
EffectsNoRuntime, sym::EffectsNoRuntime, effects_no_runtime, Target::Struct, GenericRequirement::None;
EffectsMaybe, sym::EffectsMaybe, effects_maybe, Target::Struct, GenericRequirement::None;
EffectsIntersection, sym::EffectsIntersection, effects_intersection, Target::Trait, GenericRequirement::None;
EffectsIntersectionOutput, sym::EffectsIntersectionOutput, effects_intersection_output, Target::AssocTy, GenericRequirement::None;
EffectsCompat, sym::EffectsCompat, effects_compat, Target::Trait, GenericRequirement::Exact(1);
EffectsTyCompat, sym::EffectsTyCompat, effects_ty_compat, Target::Trait, GenericRequirement::Exact(1);
}
pub enum GenericRequirement {

View File

@ -3,13 +3,8 @@
use rustc_data_structures::fx::FxIndexMap;
use rustc_hir::LangItem;
use rustc_hir::def::DefKind;
use rustc_middle::ty::fold::FnMutDelegate;
use rustc_middle::ty::{self, Ty, TyCtxt, Upcast};
use rustc_span::Span;
use rustc_span::def_id::DefId;
use crate::hir_ty_lowering::PredicateFilter;
/// Collects together a list of type bounds. These lists of bounds occur in many places
/// in Rust's syntax:
@ -47,12 +42,9 @@ impl<'tcx> Bounds<'tcx> {
pub(crate) fn push_trait_bound(
&mut self,
tcx: TyCtxt<'tcx>,
defining_def_id: DefId,
bound_trait_ref: ty::PolyTraitRef<'tcx>,
span: Span,
polarity: ty::PredicatePolarity,
constness: Option<ty::BoundConstness>,
predicate_filter: PredicateFilter,
) {
let clause = (
bound_trait_ref
@ -68,137 +60,6 @@ impl<'tcx> Bounds<'tcx> {
} else {
self.clauses.push(clause);
}
// FIXME(effects): Lift this out of `push_trait_bound`, and move it somewhere else.
// Perhaps moving this into `lower_poly_trait_ref`, just like we lower associated
// type bounds.
if !tcx.features().effects() {
return;
}
match predicate_filter {
PredicateFilter::SelfOnly | PredicateFilter::SelfThatDefines(_) => {
return;
}
PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => {
// Ok.
}
}
// For `T: ~const Tr` or `T: const Tr`, we need to add an additional bound on the
// associated type of `<T as Tr>` and make sure that the effect is compatible.
let compat_val = match (tcx.def_kind(defining_def_id), constness) {
// FIXME(effects): revisit the correctness of this
(_, Some(ty::BoundConstness::Const)) => tcx.consts.false_,
// body owners that can have trait bounds
(
DefKind::Const | DefKind::Fn | DefKind::AssocFn,
Some(ty::BoundConstness::ConstIfConst),
) => tcx.expected_host_effect_param_for_body(defining_def_id),
(_, None) => {
if !tcx.is_const_trait(bound_trait_ref.def_id()) {
return;
}
tcx.consts.true_
}
(DefKind::Trait, Some(ty::BoundConstness::ConstIfConst)) => {
// we are in a trait, where `bound_trait_ref` could be:
// (1) a super trait `trait Foo: ~const Bar`.
// - This generates `<Self as Foo>::Effects: TyCompat<<Self as Bar>::Effects>`
//
// (2) a where clause `where for<..> Something: ~const Bar`.
// - This generates `for<..> <Self as Foo>::Effects: TyCompat<<Something as Bar>::Effects>`
let Some(own_fx) = tcx.associated_type_for_effects(defining_def_id) else {
tcx.dcx().span_delayed_bug(span, "should not have allowed `~const` on a trait that doesn't have `#[const_trait]`");
return;
};
let own_fx_ty = Ty::new_projection(
tcx,
own_fx,
ty::GenericArgs::identity_for_item(tcx, own_fx),
);
let Some(their_fx) = tcx.associated_type_for_effects(bound_trait_ref.def_id())
else {
tcx.dcx().span_delayed_bug(span, "`~const` on trait without Effects assoc");
return;
};
let their_fx_ty =
Ty::new_projection(tcx, their_fx, bound_trait_ref.skip_binder().args);
let compat = tcx.require_lang_item(LangItem::EffectsTyCompat, Some(span));
let clause = bound_trait_ref
.map_bound(|_| {
let trait_ref = ty::TraitRef::new(tcx, compat, [own_fx_ty, their_fx_ty]);
ty::ClauseKind::Trait(ty::TraitPredicate {
trait_ref,
polarity: ty::PredicatePolarity::Positive,
})
})
.upcast(tcx);
self.clauses.push((clause, span));
return;
}
(DefKind::Impl { of_trait: true }, Some(ty::BoundConstness::ConstIfConst)) => {
// this is a where clause on an impl header.
// push `<T as Tr>::Effects` into the set for the `Min` bound.
let Some(assoc) = tcx.associated_type_for_effects(bound_trait_ref.def_id()) else {
tcx.dcx().span_delayed_bug(span, "`~const` on trait without Effects assoc");
return;
};
let ty = bound_trait_ref
.map_bound(|trait_ref| Ty::new_projection(tcx, assoc, trait_ref.args));
// When the user has written `for<'a, T> X<'a, T>: ~const Foo`, replace the
// binders to dummy ones i.e. `X<'static, ()>` so they can be referenced in
// the `Min` associated type properly (which doesn't allow using `for<>`)
// This should work for any bound variables as long as they don't have any
// bounds e.g. `for<T: Trait>`.
// FIXME(effects) reconsider this approach to allow compatibility with `for<T: Tr>`
let ty = tcx.replace_bound_vars_uncached(ty, FnMutDelegate {
regions: &mut |_| tcx.lifetimes.re_static,
types: &mut |_| tcx.types.unit,
consts: &mut |_| unimplemented!("`~const` does not support const binders"),
});
self.effects_min_tys.insert(ty, span);
return;
}
// for
// ```
// trait Foo { type Bar: ~const Trait }
// ```
// ensure that `<Self::Bar as Trait>::Effects: TyCompat<Self::Effects>`.
//
// FIXME(effects) this is equality for now, which wouldn't be helpful for a non-const implementor
// that uses a `Bar` that implements `Trait` with `Maybe` effects.
(DefKind::AssocTy, Some(ty::BoundConstness::ConstIfConst)) => {
// FIXME(effects): implement this
return;
}
// probably illegal in this position.
(_, Some(ty::BoundConstness::ConstIfConst)) => {
tcx.dcx().span_delayed_bug(span, "invalid `~const` encountered");
return;
}
};
// create a new projection type `<T as Tr>::Effects`
let Some(assoc) = tcx.associated_type_for_effects(bound_trait_ref.def_id()) else {
tcx.dcx().span_delayed_bug(
span,
"`~const` trait bound has no effect assoc yet no errors encountered?",
);
return;
};
let self_ty = Ty::new_projection(tcx, assoc, bound_trait_ref.skip_binder().args);
// make `<T as Tr>::Effects: Compat<runtime>`
let new_trait_ref =
ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::EffectsCompat, Some(span)), [
ty::GenericArg::from(self_ty),
compat_val.into(),
]);
self.clauses.push((bound_trait_ref.rebind(new_trait_ref).upcast(tcx), span));
}
pub(crate) fn push_projection_bound(
@ -220,6 +81,21 @@ impl<'tcx> Bounds<'tcx> {
self.clauses.insert(0, (trait_ref.upcast(tcx), span));
}
/// Push a `const` or `~const` bound as a `HostEffect` predicate.
pub(crate) fn push_const_bound(
&mut self,
tcx: TyCtxt<'tcx>,
bound_trait_ref: ty::PolyTraitRef<'tcx>,
host: ty::HostPolarity,
span: 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(
&self,
// FIXME(effects): remove tcx

View File

@ -181,6 +181,7 @@ fn compare_method_predicate_entailment<'tcx>(
});
// Create mapping from trait method to impl method.
let impl_def_id = impl_m.container_id(tcx);
let trait_to_impl_args = GenericArgs::identity_for_item(tcx, impl_m.def_id).rebase_onto(
tcx,
impl_m.container_id(tcx),
@ -204,6 +205,24 @@ fn compare_method_predicate_entailment<'tcx>(
trait_m_predicates.instantiate_own(tcx, trait_to_impl_args).map(|(predicate, _)| predicate),
);
// FIXME(effects): This should be replaced with a more dedicated method.
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(
tcx.const_conditions(impl_def_id)
.instantiate_identity(tcx)
.into_iter()
.chain(
tcx.const_conditions(trait_m.def_id).instantiate_own(tcx, trait_to_impl_args),
)
.map(|(trait_ref, _)| {
trait_ref.to_host_effect_clause(tcx, ty::HostPolarity::Maybe)
}),
);
}
let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_def_id);
let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds), Reveal::UserFacing);
let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause);
@ -230,6 +249,34 @@ fn compare_method_predicate_entailment<'tcx>(
ocx.register_obligation(traits::Obligation::new(tcx, cause, param_env, predicate));
}
// If we're within a const implementation, we need to make sure that the method
// does not assume stronger `~const` bounds than the trait definition.
//
// 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 is_conditionally_const {
for (const_condition, span) in
tcx.const_conditions(impl_m.def_id).instantiate_own_identity()
{
let normalize_cause = traits::ObligationCause::misc(span, impl_m_def_id);
let const_condition = ocx.normalize(&normalize_cause, param_env, const_condition);
let cause =
ObligationCause::new(span, impl_m_def_id, ObligationCauseCode::CompareImplItem {
impl_item_def_id: impl_m_def_id,
trait_item_def_id: trait_m.def_id,
kind: impl_m.kind,
});
ocx.register_obligation(traits::Obligation::new(
tcx,
cause,
param_env,
const_condition.to_host_effect_clause(tcx, ty::HostPolarity::Maybe),
));
}
}
// We now need to check that the signature of the impl method is
// compatible with that of the trait method. We do this by
// checking that `impl_fty <: trait_fty`.
@ -1846,9 +1893,10 @@ fn compare_type_predicate_entailment<'tcx>(
trait_ty: ty::AssocItem,
impl_trait_ref: ty::TraitRef<'tcx>,
) -> Result<(), ErrorGuaranteed> {
let impl_def_id = impl_ty.container_id(tcx);
let trait_to_impl_args = GenericArgs::identity_for_item(tcx, impl_ty.def_id).rebase_onto(
tcx,
impl_ty.container_id(tcx),
impl_def_id,
impl_trait_ref.args,
);
@ -1856,6 +1904,7 @@ 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();
// 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(());
@ -1881,6 +1930,23 @@ 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 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(
tcx.const_conditions(impl_ty_predicates.parent.unwrap())
.instantiate_identity(tcx)
.into_iter()
.chain(
tcx.const_conditions(trait_ty.def_id).instantiate_own(tcx, trait_to_impl_args),
)
.map(|(trait_ref, _)| {
trait_ref.to_host_effect_clause(tcx, ty::HostPolarity::Maybe)
}),
);
}
let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds), Reveal::UserFacing);
let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause);
debug!(caller_bounds=?param_env.caller_bounds());
@ -1901,6 +1967,29 @@ fn compare_type_predicate_entailment<'tcx>(
ocx.register_obligation(traits::Obligation::new(tcx, cause, param_env, predicate));
}
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);
let cause =
ObligationCause::new(span, impl_ty_def_id, ObligationCauseCode::CompareImplItem {
impl_item_def_id: impl_ty_def_id,
trait_item_def_id: trait_ty.def_id,
kind: impl_ty.kind,
});
ocx.register_obligation(traits::Obligation::new(
tcx,
cause,
param_env,
const_condition.to_host_effect_clause(tcx, ty::HostPolarity::Maybe),
));
}
}
// Check that all obligations are satisfied by the implementation's
// version.
let errors = ocx.select_all_or_error();
@ -1983,7 +2072,7 @@ pub(super) fn check_type_bounds<'tcx>(
ObligationCause::new(impl_ty_span, impl_ty_def_id, code)
};
let obligations: Vec<_> = tcx
let mut obligations: Vec<_> = tcx
.explicit_item_bounds(trait_ty.def_id)
.iter_instantiated_copied(tcx, rebased_args)
.map(|(concrete_ty_bound, span)| {
@ -1991,6 +2080,22 @@ pub(super) fn check_type_bounds<'tcx>(
traits::Obligation::new(tcx, mk_cause(span), param_env, concrete_ty_bound)
})
.collect();
// Only in a const implementation do we need to check that the `~const` item bounds hold.
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)
.map(|(c, span)| {
traits::Obligation::new(
tcx,
mk_cause(span),
param_env,
c.to_host_effect_clause(tcx, ty::HostPolarity::Maybe),
)
}),
);
}
debug!(item_bounds=?obligations);
// Normalize predicates with the assumption that the GAT may always normalize

View File

@ -58,15 +58,9 @@ fn equate_intrinsic_type<'tcx>(
// the host effect param should be invisible as it shouldn't matter
// whether effects is enabled for the intrinsic provider crate.
let consts_count = if generics.host_effect_index.is_some() {
own_counts.consts - 1
} else {
own_counts.consts
};
if gen_count_ok(own_counts.lifetimes, n_lts, "lifetime")
&& gen_count_ok(own_counts.types, n_tps, "type")
&& gen_count_ok(consts_count, n_cts, "const")
&& gen_count_ok(own_counts.consts, n_cts, "const")
{
let _ = check_function_signature(
tcx,

View File

@ -32,7 +32,8 @@ use rustc_trait_selection::traits::misc::{
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
use rustc_trait_selection::traits::{
self, FulfillmentError, ObligationCause, ObligationCauseCode, ObligationCtxt, WellFormedLoc,
self, FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, ObligationCtxt,
WellFormedLoc,
};
use rustc_type_ir::TypeFlags;
use rustc_type_ir::solve::NoSolution;
@ -86,7 +87,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
self.body_def_id,
ObligationCauseCode::WellFormed(loc),
);
self.ocx.register_obligation(traits::Obligation::new(
self.ocx.register_obligation(Obligation::new(
self.tcx(),
cause,
self.param_env,
@ -913,12 +914,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(),
hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. } => Ok(()),
// Const parameters are well formed if their type is structural match.
hir::GenericParamKind::Const {
ty: hir_ty,
default: _,
is_host_effect: _,
synthetic: _,
} => {
hir::GenericParamKind::Const { ty: hir_ty, default: _, synthetic: _ } => {
let ty = tcx.type_of(param.def_id).instantiate_identity();
if tcx.features().unsized_const_params() {
@ -1178,7 +1174,7 @@ fn check_type_defn<'tcx>(
wfcx.body_def_id,
ObligationCauseCode::Misc,
);
wfcx.register_obligation(traits::Obligation::new(
wfcx.register_obligation(Obligation::new(
tcx,
cause,
wfcx.param_env,
@ -1374,6 +1370,30 @@ fn check_impl<'tcx>(
obligation.cause.span = hir_self_ty.span;
}
}
// Ensure that the `~const` where clauses of the trait hold for the impl.
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)
{
let bound = wfcx.normalize(
item.span,
Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)),
bound,
);
wfcx.register_obligation(Obligation::new(
tcx,
ObligationCause::new(
hir_self_ty.span,
wfcx.body_def_id,
ObligationCauseCode::WellFormed(None),
),
wfcx.param_env,
bound.to_host_effect_clause(tcx, ty::HostPolarity::Maybe),
))
}
}
debug!(?obligations);
wfcx.register_obligations(obligations);
}
@ -1566,7 +1586,7 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
wfcx.body_def_id,
ObligationCauseCode::WhereClause(def_id.to_def_id(), DUMMY_SP),
);
traits::Obligation::new(tcx, cause, wfcx.param_env, pred)
Obligation::new(tcx, cause, wfcx.param_env, pred)
});
let predicates = predicates.instantiate_identity(tcx);
@ -1857,7 +1877,7 @@ fn receiver_is_implemented<'tcx>(
let tcx = wfcx.tcx();
let trait_ref = ty::TraitRef::new(tcx, receiver_trait_def_id, [receiver_ty]);
let obligation = traits::Obligation::new(tcx, cause, wfcx.param_env, trait_ref);
let obligation = Obligation::new(tcx, cause, wfcx.param_env, trait_ref);
if wfcx.infcx.predicate_must_hold_modulo_regions(&obligation) {
true
@ -2193,7 +2213,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
.unwrap_or(obligation_span);
}
let obligation = traits::Obligation::new(
let obligation = Obligation::new(
tcx,
traits::ObligationCause::new(
span,

View File

@ -77,6 +77,8 @@ pub fn provide(providers: &mut Providers) {
explicit_supertraits_containing_assoc_item:
predicates_of::explicit_supertraits_containing_assoc_item,
trait_explicit_predicates_and_bounds: predicates_of::trait_explicit_predicates_and_bounds,
const_conditions: predicates_of::const_conditions,
implied_const_bounds: predicates_of::implied_const_bounds,
type_param_predicates: predicates_of::type_param_predicates,
trait_def,
adt_def,

View File

@ -53,7 +53,6 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
param_def_id_to_index,
has_self: opaque_ty_generics.has_self,
has_late_bound_regions: opaque_ty_generics.has_late_bound_regions,
host_effect_index: parent_generics.host_effect_index,
};
}
@ -161,7 +160,6 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
param_def_id_to_index,
has_self: generics.has_self,
has_late_bound_regions: generics.has_late_bound_regions,
host_effect_index: None,
};
} else {
// HACK(eddyb) this provides the correct generics when
@ -292,12 +290,10 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
let has_self = opt_self.is_some();
let mut parent_has_self = false;
let mut own_start = has_self as u32;
let mut host_effect_index = None;
let parent_count = parent_def_id.map_or(0, |def_id| {
let generics = tcx.generics_of(def_id);
assert!(!has_self);
parent_has_self = generics.has_self;
host_effect_index = generics.host_effect_index;
own_start = generics.count() as u32;
generics.parent_count + generics.own_params.len()
});
@ -361,12 +357,8 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
kind,
})
}
GenericParamKind::Const { ty: _, default, is_host_effect, synthetic } => {
if !matches!(allow_defaults, Defaults::Allowed)
&& default.is_some()
// `host` effect params are allowed to have defaults.
&& !is_host_effect
{
GenericParamKind::Const { ty: _, default, synthetic } => {
if !matches!(allow_defaults, Defaults::Allowed) && default.is_some() {
tcx.dcx().span_err(
param.span,
"defaults for const parameters are only allowed in \
@ -376,27 +368,12 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
let index = next_index();
if is_host_effect {
if let Some(idx) = host_effect_index {
tcx.dcx().span_delayed_bug(
param.span,
format!("parent also has host effect param? index: {idx}, def: {def_id:?}"),
);
}
host_effect_index = Some(index as usize);
}
Some(ty::GenericParamDef {
index,
name: param.name.ident().name,
def_id: param.def_id.to_def_id(),
pure_wrt_drop: param.pure_wrt_drop,
kind: ty::GenericParamDefKind::Const {
has_default: default.is_some(),
is_host_effect,
synthetic,
},
kind: ty::GenericParamDefKind::Const { has_default: default.is_some(), synthetic },
})
}
}));
@ -459,7 +436,6 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
param_def_id_to_index,
has_self: has_self || parent_has_self,
has_late_bound_regions: has_late_bound_regions(tcx, node),
host_effect_index,
}
}
@ -540,8 +516,7 @@ impl<'v> Visitor<'v> for AnonConstInParamTyDetector {
type Result = ControlFlow<()>;
fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) -> Self::Result {
if let GenericParamKind::Const { ty, default: _, is_host_effect: _, synthetic: _ } = p.kind
{
if let GenericParamKind::Const { ty, default: _, synthetic: _ } = p.kind {
let prev = self.in_param_ty;
self.in_param_ty = true;
let res = self.visit_ty(ty);

View File

@ -40,7 +40,16 @@ fn associated_type_bounds<'tcx>(
let mut bounds = Bounds::default();
icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter);
// Associated types are implicitly sized unless a `?Sized` bound is found
icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span);
match filter {
PredicateFilter::All
| PredicateFilter::SelfOnly
| PredicateFilter::SelfThatDefines(_)
| PredicateFilter::SelfAndAssociatedTypeBounds => {
icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span);
}
// `ConstIfConst` is only interested in `~const` bounds.
PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {}
}
let trait_def_id = tcx.local_parent(assoc_item_def_id);
let trait_predicates = tcx.trait_explicit_predicates_and_bounds(trait_def_id);
@ -109,10 +118,19 @@ fn remap_gat_vars_and_recurse_into_nested_projections<'tcx>(
} else {
// Only collect *self* type bounds if the filter is for self.
match filter {
PredicateFilter::SelfOnly | PredicateFilter::SelfThatDefines(_) => {
PredicateFilter::All => {}
PredicateFilter::SelfOnly => {
return None;
}
PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => {}
PredicateFilter::SelfThatDefines(_)
| PredicateFilter::SelfConstIfConst
| PredicateFilter::SelfAndAssociatedTypeBounds
| PredicateFilter::ConstIfConst => {
unreachable!(
"invalid predicate filter for \
`remap_gat_vars_and_recurse_into_nested_projections`"
)
}
}
clause_ty = alias_ty.self_ty();
@ -308,7 +326,17 @@ fn opaque_type_bounds<'tcx>(
let mut bounds = Bounds::default();
icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter);
// Opaque types are implicitly sized unless a `?Sized` bound is found
icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span);
match filter {
PredicateFilter::All
| PredicateFilter::SelfOnly
| PredicateFilter::SelfThatDefines(_)
| PredicateFilter::SelfAndAssociatedTypeBounds => {
// Associated types are implicitly sized unless a `?Sized` bound is found
icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span);
}
//`ConstIfConst` is only interested in `~const` bounds.
PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {}
}
debug!(?bounds);
tcx.arena.alloc_from_iter(bounds.clauses(tcx))

View File

@ -12,6 +12,7 @@ use rustc_span::symbol::Ident;
use rustc_span::{DUMMY_SP, Span};
use tracing::{debug, instrument, trace};
use super::item_bounds::explicit_item_bounds_with_filter;
use crate::bounds::Bounds;
use crate::collect::ItemCtxt;
use crate::constrained_generic_params as cgp;
@ -78,7 +79,6 @@ pub(super) fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredic
#[instrument(level = "trace", skip(tcx), ret)]
fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::GenericPredicates<'_> {
use rustc_hir::*;
use rustc_middle::ty::Ty;
match tcx.opt_rpitit_info(def_id.to_def_id()) {
Some(ImplTraitInTraitData::Trait { fn_def_id, .. }) => {
@ -345,26 +345,6 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
debug!(?predicates);
}
// add `Self::Effects: Compat<HOST>` to ensure non-const impls don't get called
// in const contexts.
if let Node::TraitItem(&TraitItem { kind: TraitItemKind::Fn(..), .. }) = node
&& let Some(host_effect_index) = generics.host_effect_index
{
let parent = generics.parent.unwrap();
let Some(assoc_def_id) = tcx.associated_type_for_effects(parent) else {
bug!("associated_type_for_effects returned None when there is host effect in generics");
};
let effects =
Ty::new_projection(tcx, assoc_def_id, ty::GenericArgs::identity_for_item(tcx, parent));
let param = generics.param_at(host_effect_index, tcx);
let span = tcx.def_span(param.def_id);
let host = ty::Const::new_param(tcx, ty::ParamConst::for_def(param));
let compat = tcx.require_lang_item(LangItem::EffectsCompat, Some(span));
let trait_ref =
ty::TraitRef::new(tcx, compat, [ty::GenericArg::from(effects), host.into()]);
predicates.push((ty::Binder::dummy(trait_ref).upcast(tcx), span));
}
ty::GenericPredicates {
parent: generics.parent,
predicates: tcx.arena.alloc_from_iter(predicates),
@ -706,29 +686,76 @@ pub(super) fn assert_only_contains_predicates_from<'tcx>(
assert_eq!(
trait_predicate.self_ty(),
ty,
"expected `Self` predicate when computing `{filter:?}` implied bounds: {clause:?}"
"expected `Self` predicate when computing \
`{filter:?}` implied bounds: {clause:?}"
);
}
ty::ClauseKind::Projection(projection_predicate) => {
assert_eq!(
projection_predicate.self_ty(),
ty,
"expected `Self` predicate when computing `{filter:?}` implied bounds: {clause:?}"
"expected `Self` predicate when computing \
`{filter:?}` implied bounds: {clause:?}"
);
}
ty::ClauseKind::TypeOutlives(outlives_predicate) => {
assert_eq!(
outlives_predicate.0, ty,
"expected `Self` predicate when computing `{filter:?}` implied bounds: {clause:?}"
"expected `Self` predicate when computing \
`{filter:?}` implied bounds: {clause:?}"
);
}
ty::ClauseKind::RegionOutlives(_)
| ty::ClauseKind::ConstArgHasType(_, _)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(_) => {
| ty::ClauseKind::ConstEvaluatable(_)
| ty::ClauseKind::HostEffect(..) => {
bug!(
"unexpected non-`Self` predicate when computing `{filter:?}` implied bounds: {clause:?}"
"unexpected non-`Self` predicate when computing \
`{filter:?}` implied bounds: {clause:?}"
);
}
}
}
}
PredicateFilter::ConstIfConst => {
for (clause, _) in bounds {
match clause.kind().skip_binder() {
ty::ClauseKind::HostEffect(ty::HostEffectPredicate {
trait_ref: _,
host: ty::HostPolarity::Maybe,
}) => {}
_ => {
bug!(
"unexpected non-`HostEffect` predicate when computing \
`{filter:?}` implied bounds: {clause:?}"
);
}
}
}
}
PredicateFilter::SelfConstIfConst => {
for (clause, _) in bounds {
match clause.kind().skip_binder() {
ty::ClauseKind::HostEffect(pred) => {
assert_eq!(
pred.host,
ty::HostPolarity::Maybe,
"expected `~const` predicate when computing `{filter:?}` \
implied bounds: {clause:?}",
);
assert_eq!(
pred.trait_ref.self_ty(),
ty,
"expected `Self` predicate when computing `{filter:?}` \
implied bounds: {clause:?}"
);
}
_ => {
bug!(
"unexpected non-`HostEffect` predicate when computing \
`{filter:?}` implied bounds: {clause:?}"
);
}
}
@ -850,3 +877,144 @@ impl<'tcx> ItemCtxt<'tcx> {
bounds.clauses(self.tcx).collect()
}
}
/// Compute the conditions that need to hold for a conditionally-const item to be const.
/// That is, compute the set of `~const` where clauses for a given item.
///
/// This query also computes the `~const` where clauses for associated types, which are
/// not "const", but which have item bounds which may be `~const`. These must hold for
/// the `~const` item bound to hold.
pub(super) fn const_conditions<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
) -> ty::ConstConditions<'tcx> {
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)
{
Node::Item(item) => match item.kind {
hir::ItemKind::Impl(impl_) => (impl_.generics, None, false),
hir::ItemKind::Fn(_, generics, _) => (generics, None, false),
hir::ItemKind::Trait(_, _, generics, supertraits, _) => {
(generics, Some((item.owner_id.def_id, supertraits)), false)
}
_ => 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
// these up so we can check them in `compare_type_predicate_entailment`, and
// in `HostEffect` goal computation.
Node::TraitItem(item) => match item.kind {
hir::TraitItemKind::Fn(_, _) | hir::TraitItemKind::Type(_, _) => {
(item.generics, None, true)
}
_ => bug!("const_conditions called on wrong item: {def_id:?}"),
},
Node::ImplItem(item) => match item.kind {
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:?}"),
},
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);
let mut bounds = Bounds::default();
for pred in generics.predicates {
match pred {
hir::WherePredicate::BoundPredicate(bound_pred) => {
let ty = icx.lowerer().lower_ty_maybe_return_type_notation(bound_pred.bounded_ty);
let bound_vars = tcx.late_bound_vars(bound_pred.hir_id);
icx.lowerer().lower_bounds(
ty,
bound_pred.bounds.iter(),
&mut bounds,
bound_vars,
PredicateFilter::ConstIfConst,
);
}
_ => {}
}
}
if let Some((def_id, supertraits)) = trait_def_id_and_supertraits {
bounds.push_const_bound(
tcx,
ty::Binder::dummy(ty::TraitRef::identity(tcx, def_id.to_def_id())),
ty::HostPolarity::Maybe,
DUMMY_SP,
);
icx.lowerer().lower_bounds(
tcx.types.self_param,
supertraits.into_iter(),
&mut bounds,
ty::List::empty(),
PredicateFilter::ConstIfConst,
);
}
ty::ConstConditions {
parent: has_parent.then(|| tcx.local_parent(def_id).to_def_id()),
predicates: tcx.arena.alloc_from_iter(bounds.clauses(tcx).map(|(clause, span)| {
(
clause.kind().map_bound(|clause| match clause {
ty::ClauseKind::HostEffect(ty::HostEffectPredicate {
trait_ref,
host: ty::HostPolarity::Maybe,
}) => trait_ref,
_ => bug!("converted {clause:?}"),
}),
span,
)
})),
}
}
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(..), .. }) => {
implied_predicates_with_filter(
tcx,
def_id.to_def_id(),
PredicateFilter::SelfConstIfConst,
)
}
Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Type(..), .. }) => {
explicit_item_bounds_with_filter(tcx, def_id, PredicateFilter::ConstIfConst)
}
_ => bug!("implied_const_bounds called on wrong item: {def_id:?}"),
};
bounds.map_bound(|bounds| {
&*tcx.arena.alloc_from_iter(bounds.iter().copied().map(|(clause, span)| {
(
clause.kind().map_bound(|clause| match clause {
ty::ClauseKind::HostEffect(ty::HostEffectPredicate {
trait_ref,
host: ty::HostPolarity::Maybe,
}) => trait_ref,
_ => bug!("converted {clause:?}"),
}),
span,
)
}))
})
}

View File

@ -186,7 +186,6 @@ impl<'tcx> GenericsBuilder<'tcx> {
param_def_id_to_index,
has_self,
has_late_bound_regions: sig_generics.has_late_bound_regions,
host_effect_index: sig_generics.host_effect_index,
}
}
}
@ -472,10 +471,6 @@ fn check_constraints<'tcx>(
}));
};
if tcx.has_host_param(sig_id) {
emit("delegation to a function with effect parameter is not supported yet");
}
if let Some(local_sig_id) = sig_id.as_local()
&& tcx.hir().opt_delegation_sig_id(local_sig_id).is_some()
{

View File

@ -154,7 +154,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
for hir_bound in hir_bounds {
// In order to avoid cycles, when we're lowering `SelfThatDefines`,
// we skip over any traits that don't define the given associated type.
if let PredicateFilter::SelfThatDefines(assoc_name) = predicate_filter {
if let Some(trait_ref) = hir_bound.trait_ref()
&& let Some(trait_did) = trait_ref.trait_def_id()
@ -193,6 +192,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
);
}
hir::GenericBound::Outlives(lifetime) => {
// `ConstIfConst` is only interested in `~const` bounds.
if matches!(
predicate_filter,
PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst
) {
continue;
}
let region = self.lower_lifetime(lifetime, RegionInferReason::OutlivesBound);
bounds.push_region_bound(
self.tcx(),
@ -392,21 +399,31 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
},
);
bounds.push_projection_bound(
tcx,
projection_term.map_bound(|projection_term| ty::ProjectionPredicate {
projection_term,
term,
}),
constraint.span,
);
match predicate_filter {
PredicateFilter::All
| PredicateFilter::SelfOnly
| PredicateFilter::SelfThatDefines(_)
| PredicateFilter::SelfAndAssociatedTypeBounds => {
bounds.push_projection_bound(
tcx,
projection_term.map_bound(|projection_term| ty::ProjectionPredicate {
projection_term,
term,
}),
constraint.span,
);
}
// `ConstIfConst` is only interested in `~const` bounds.
PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {}
}
}
// Lower a constraint like `Item: Debug` as found in HIR bound `T: Iterator<Item: Debug>`
// to a bound involving a projection: `<T as Iterator>::Item: Debug`.
hir::AssocItemConstraintKind::Bound { bounds: hir_bounds } => {
match predicate_filter {
PredicateFilter::SelfOnly | PredicateFilter::SelfThatDefines(_) => {}
PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => {
PredicateFilter::All
| PredicateFilter::SelfAndAssociatedTypeBounds
| PredicateFilter::ConstIfConst => {
let projection_ty = projection_term
.map_bound(|projection_term| projection_term.expect_ty(self.tcx()));
// Calling `skip_binder` is okay, because `lower_bounds` expects the `param_ty`
@ -421,6 +438,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
predicate_filter,
);
}
PredicateFilter::SelfOnly
| PredicateFilter::SelfThatDefines(_)
| PredicateFilter::SelfConstIfConst => {}
}
}
}

View File

@ -78,7 +78,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
ty::ClauseKind::RegionOutlives(_)
| ty::ClauseKind::ConstArgHasType(..)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(_) => {
| ty::ClauseKind::ConstEvaluatable(_)
| ty::ClauseKind::HostEffect(..) => {
span_bug!(span, "did not expect {pred} clause in object bounds");
}
}

View File

@ -81,6 +81,12 @@ pub enum PredicateFilter {
/// For example, given `Self: Tr<A: B>`, this would expand to `Self: Tr`
/// and `<Self as Tr>::A: B`.
SelfAndAssociatedTypeBounds,
/// Filter only the `~const` bounds, which are lowered into `HostEffect` clauses.
ConstIfConst,
/// Filter only the `~const` bounds which are *also* in the supertrait position.
SelfConstIfConst,
}
#[derive(Debug)]
@ -693,16 +699,49 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
bound_vars,
);
debug!(?poly_trait_ref);
bounds.push_trait_bound(
tcx,
self.item_def_id().to_def_id(),
poly_trait_ref,
span,
polarity,
constness,
predicate_filter,
);
match predicate_filter {
PredicateFilter::All
| PredicateFilter::SelfOnly
| PredicateFilter::SelfThatDefines(..)
| PredicateFilter::SelfAndAssociatedTypeBounds => {
debug!(?poly_trait_ref);
bounds.push_trait_bound(tcx, poly_trait_ref, span, polarity);
match constness {
Some(ty::BoundConstness::Const) => {
if polarity == ty::PredicatePolarity::Positive {
bounds.push_const_bound(
tcx,
poly_trait_ref,
ty::HostPolarity::Const,
span,
);
}
}
Some(ty::BoundConstness::ConstIfConst) => {
// We don't emit a const bound here, since that would mean that we
// unconditionally need to prove a `HostEffect` predicate, even when
// the predicates are being instantiated in a non-const context. This
// is instead handled in the `const_conditions` query.
}
None => {}
}
}
// On the flip side, when filtering `ConstIfConst` bounds, we only need to convert
// `~const` bounds. All other predicates are handled in their respective queries.
//
// Note that like `PredicateFilter::SelfOnly`, we don't need to do any filtering
// here because we only call this on self bounds, and deal with the recursive case
// in `lower_assoc_item_constraint`.
PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => match constness {
Some(ty::BoundConstness::ConstIfConst) => {
if polarity == ty::PredicatePolarity::Positive {
bounds.push_const_bound(tcx, poly_trait_ref, ty::HostPolarity::Maybe, span);
}
}
None | Some(ty::BoundConstness::Const) => {}
},
}
let mut dup_constraints = FxIndexMap::default();
for constraint in trait_segment.args().constraints {

View File

@ -530,6 +530,7 @@ fn trait_specialization_kind<'tcx>(
| ty::ClauseKind::Projection(_)
| ty::ClauseKind::ConstArgHasType(..)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(..) => None,
| ty::ClauseKind::ConstEvaluatable(..)
| ty::ClauseKind::HostEffect(..) => None,
}
}

View File

@ -53,7 +53,8 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> {
| ty::ClauseKind::Projection(_)
| ty::ClauseKind::ConstArgHasType(_, _)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(_) => {}
| ty::ClauseKind::ConstEvaluatable(_)
| ty::ClauseKind::HostEffect(..) => {}
}
}

View File

@ -2134,7 +2134,7 @@ impl<'a> State<'a> {
self.print_type(default);
}
}
GenericParamKind::Const { ty, ref default, is_host_effect: _, synthetic: _ } => {
GenericParamKind::Const { ty, ref default, synthetic: _ } => {
self.word_space(":");
self.print_type(ty);
if let Some(default) = default {

View File

@ -20,7 +20,7 @@ use rustc_target::spec::abi;
use rustc_trait_selection::error_reporting::traits::DefIdOrName;
use rustc_trait_selection::infer::InferCtxtExt as _;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
use tracing::{debug, instrument, trace};
use tracing::{debug, instrument};
use super::method::MethodCallee;
use super::method::probe::ProbeScope;
@ -843,29 +843,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
callee_did: DefId,
callee_args: GenericArgsRef<'tcx>,
) {
let tcx = self.tcx;
// FIXME(effects): We should be enforcing these effects unconditionally.
// This can be done as soon as we convert the standard library back to
// using const traits, since if we were to enforce these conditions now,
// we'd fail on basically every builtin trait call (i.e. `1 + 2`).
if !self.tcx.features().effects() {
return;
}
// fast-reject if callee doesn't have the host effect param (non-const)
let generics = tcx.generics_of(callee_did);
let Some(host_effect_index) = generics.host_effect_index else { return };
let effect = tcx.expected_host_effect_param_for_body(self.body_id);
trace!(?effect, ?generics, ?callee_args);
let param = callee_args.const_at(host_effect_index);
let cause = self.misc(span);
// We know the type of `effect` to be `bool`, there will be no opaque type inference.
match self.at(&cause, self.param_env).eq(infer::DefineOpaqueTypes::Yes, effect, param) {
Ok(infer::InferOk { obligations, value: () }) => {
self.register_predicates(obligations);
let host = match self.tcx.hir().body_const_context(self.body_id) {
Some(hir::ConstContext::Const { .. } | hir::ConstContext::Static(_)) => {
ty::HostPolarity::Const
}
Err(e) => {
// FIXME(effects): better diagnostic
self.err_ctxt()
.report_mismatched_consts(&cause, self.param_env, effect, param, e)
.emit();
Some(hir::ConstContext::ConstFn) => ty::HostPolarity::Maybe,
None => return,
};
// 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_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) {
self.register_predicate(Obligation::new(
self.tcx,
self.misc(span),
self.param_env,
cond.to_host_effect_clause(self.tcx, host),
));
}
} else {
// FIXME(effects): This should eventually be caught here.
// For now, though, we defer some const checking to MIR.
}
}

View File

@ -7,8 +7,6 @@ use rustc_data_structures::unord::{UnordBag, UnordMap, UnordSet};
use rustc_hir as hir;
use rustc_hir::HirId;
use rustc_hir::intravisit::Visitor;
use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
use rustc_middle::bug;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
use rustc_session::lint;
use rustc_span::def_id::LocalDefId;
@ -48,7 +46,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
self.fulfillment_cx.borrow_mut().pending_obligations()
);
let fallback_occurred = self.fallback_types() | self.fallback_effects();
let fallback_occurred = self.fallback_types();
if !fallback_occurred {
return;
@ -103,31 +101,6 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
fallback_occurred
}
fn fallback_effects(&self) -> bool {
let unsolved_effects = self.unsolved_effects();
if unsolved_effects.is_empty() {
return false;
}
// not setting the `fallback_has_occurred` field here because
// that field is only used for type fallback diagnostics.
for effect in unsolved_effects {
let expected = self.tcx.consts.true_;
let cause = self.misc(DUMMY_SP);
match self.at(&cause, self.param_env).eq(DefineOpaqueTypes::Yes, expected, effect) {
Ok(InferOk { obligations, value: () }) => {
self.register_predicates(obligations);
}
Err(e) => {
bug!("cannot eq unsolved effect: {e:?}")
}
}
}
true
}
// Tries to apply a fallback to `ty` if it is an unsolved variable.
//
// - Unconstrained ints are replaced with `i32`.

View File

@ -1262,15 +1262,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
(GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => {
self.fcx.ty_infer(Some(param), inf.span).into()
}
(
&GenericParamDefKind::Const { has_default, is_host_effect, .. },
GenericArg::Infer(inf),
) => {
if has_default && is_host_effect {
self.fcx.var_for_effect(param)
} else {
self.fcx.ct_infer(Some(param), inf.span).into()
}
(&GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => {
self.fcx.ct_infer(Some(param), inf.span).into()
}
_ => unreachable!(),
}
@ -1305,20 +1298,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.fcx.var_for_def(self.span, param)
}
}
GenericParamDefKind::Const { has_default, is_host_effect, .. } => {
GenericParamDefKind::Const { has_default, .. } => {
if has_default {
// N.B. this is a bit of a hack. `infer_args` is passed depending on
// whether the user has provided generic args. E.g. for `Vec::new`
// we would have to infer the generic types. However, for `Vec::<T>::new`
// where the allocator param `A` has a default we will *not* infer. But
// for effect params this is a different story: if the user has not written
// anything explicit for the effect param, we always need to try to infer
// it before falling back to default, such that a `const fn` such as
// `needs_drop::<()>` can still be called in const contexts. (if we defaulted
// instead of inferred, typeck would error)
if is_host_effect {
return self.fcx.var_for_effect(param);
} else if !infer_args {
if !infer_args {
return tcx
.const_param_default(param.def_id)
.instantiate(tcx, preceding_args)

View File

@ -52,6 +52,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
| ty::PredicateKind::AliasRelate(..)
| ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..))
| ty::PredicateKind::Ambiguous => false,
}
}

View File

@ -247,12 +247,6 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> {
fn ct_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Const<'tcx> {
// FIXME ideally this shouldn't use unwrap
match param {
Some(
param @ ty::GenericParamDef {
kind: ty::GenericParamDefKind::Const { is_host_effect: true, .. },
..
},
) => self.var_for_effect(param).as_const().unwrap(),
Some(param) => self.var_for_def(span, param).as_const().unwrap(),
None => self.next_const_var(span),
}

View File

@ -369,17 +369,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
let (obligation, args) =
self.obligation_for_method(cause, trait_def_id, self_ty, opt_input_types);
// FIXME(effects) find a better way to do this
// Operators don't have generic methods, but making them `#[const_trait]` gives them
// `const host: bool`.
let args = if self.tcx.is_const_trait(trait_def_id) {
self.tcx.mk_args_from_iter(
args.iter()
.chain([self.tcx.expected_host_effect_param_for_body(self.body_id).into()]),
)
} else {
args
};
self.construct_obligation_for_trait(m_name, trait_def_id, obligation, args)
}

View File

@ -813,7 +813,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
| ty::ClauseKind::Projection(_)
| ty::ClauseKind::ConstArgHasType(_, _)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(_) => None,
| ty::ClauseKind::ConstEvaluatable(_)
| ty::ClauseKind::HostEffect(..) => None,
}
});

View File

@ -489,17 +489,6 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
}
}
}
ty::ConstKind::Infer(InferConst::EffectVar(vid)) => {
match self.infcx.unwrap().probe_effect_var(vid) {
Some(value) => return self.fold_const(value),
None => {
return self.canonicalize_const_var(
CanonicalVarInfo { kind: CanonicalVarKind::Effect },
ct,
);
}
}
}
ty::ConstKind::Infer(InferConst::Fresh(_)) => {
bug!("encountered a fresh const during canonicalization")
}
@ -700,8 +689,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
.iter()
.map(|v| CanonicalVarInfo {
kind: match v.kind {
CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float)
| CanonicalVarKind::Effect => {
CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => {
return *v;
}
CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => {

View File

@ -24,7 +24,6 @@
pub use instantiate::CanonicalExt;
use rustc_index::IndexVec;
pub use rustc_middle::infer::canonical::*;
use rustc_middle::infer::unify_key::EffectVarValue;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::{self, GenericArg, List, Ty, TyCtxt};
use rustc_span::Span;
@ -145,15 +144,6 @@ impl<'tcx> InferCtxt<'tcx> {
CanonicalVarKind::Const(ui) => {
self.next_const_var_in_universe(span, universe_map(ui)).into()
}
CanonicalVarKind::Effect => {
let vid = self
.inner
.borrow_mut()
.effect_unification_table()
.new_key(EffectVarValue::Unknown)
.vid;
ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(vid)).into()
}
CanonicalVarKind::PlaceholderConst(ty::PlaceholderConst { universe, bound }) => {
let universe_mapped = universe_map(universe);
let placeholder_mapped = ty::PlaceholderConst { universe: universe_mapped, bound };

View File

@ -1,6 +1,5 @@
///! Definition of `InferCtxtLike` from the librarified type layer.
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::infer::unify_key::EffectVarValue;
use rustc_middle::traits::ObligationCause;
use rustc_middle::traits::solve::SolverMode;
use rustc_middle::ty::fold::TypeFoldable;
@ -88,15 +87,6 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
}
}
fn opportunistic_resolve_effect_var(&self, vid: ty::EffectVid) -> ty::Const<'tcx> {
match self.probe_effect_var(vid) {
Some(ct) => ct,
None => {
ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(self.root_effect_var(vid)))
}
}
}
fn opportunistic_resolve_lt_var(&self, vid: ty::RegionVid) -> ty::Region<'tcx> {
self.inner.borrow_mut().unwrap_region_constraints().opportunistic_resolve_var(self.tcx, vid)
}
@ -152,10 +142,6 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
self.inner.borrow_mut().const_unification_table().union(a, b);
}
fn equate_effect_vids_raw(&self, a: rustc_type_ir::EffectVid, b: rustc_type_ir::EffectVid) {
self.inner.borrow_mut().effect_unification_table().union(a, b);
}
fn instantiate_ty_var_raw<R: PredicateEmittingRelation<Self>>(
&self,
relation: &mut R,
@ -189,13 +175,6 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
self.inner.borrow_mut().float_unification_table().union_value(vid, value);
}
fn instantiate_effect_var_raw(&self, vid: rustc_type_ir::EffectVid, value: ty::Const<'tcx>) {
self.inner
.borrow_mut()
.effect_unification_table()
.union_value(vid, EffectVarValue::Known(value));
}
fn instantiate_const_var_raw<R: PredicateEmittingRelation<Self>>(
&self,
relation: &mut R,

View File

@ -153,15 +153,6 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for TypeFreshener<'a, 'tcx> {
drop(inner);
self.freshen_const(input, ty::InferConst::Fresh)
}
ty::ConstKind::Infer(ty::InferConst::EffectVar(v)) => {
let mut inner = self.infcx.inner.borrow_mut();
let input =
inner.effect_unification_table().probe_value(v).known().ok_or_else(|| {
ty::InferConst::EffectVar(inner.effect_unification_table().find(v).vid)
});
drop(inner);
self.freshen_const(input, ty::InferConst::Fresh)
}
ty::ConstKind::Infer(ty::InferConst::Fresh(i)) => {
if i >= self.const_freshen_count {
bug!(

View File

@ -26,9 +26,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_macros::extension;
pub use rustc_macros::{TypeFoldable, TypeVisitable};
use rustc_middle::infer::canonical::{CanonicalQueryInput, CanonicalVarValues};
use rustc_middle::infer::unify_key::{
ConstVariableOrigin, ConstVariableValue, ConstVidKey, EffectVarValue, EffectVidKey,
};
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey};
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult};
use rustc_middle::traits::select;
@ -39,7 +37,7 @@ use rustc_middle::ty::fold::{
};
use rustc_middle::ty::visit::TypeVisitableExt;
use rustc_middle::ty::{
self, ConstVid, EffectVid, FloatVid, GenericArg, GenericArgKind, GenericArgs, GenericArgsRef,
self, ConstVid, FloatVid, GenericArg, GenericArgKind, GenericArgs, GenericArgsRef,
GenericParamDefKind, InferConst, IntVid, Ty, TyCtxt, TyVid,
};
use rustc_middle::{bug, span_bug};
@ -117,9 +115,6 @@ pub struct InferCtxtInner<'tcx> {
/// Map from floating variable to the kind of float it represents.
float_unification_storage: ut::UnificationTableStorage<ty::FloatVid>,
/// Map from effect variable to the effect param it represents.
effect_unification_storage: ut::UnificationTableStorage<EffectVidKey<'tcx>>,
/// Tracks the set of region variables and the constraints between them.
///
/// This is initially `Some(_)` but when
@ -176,7 +171,6 @@ impl<'tcx> InferCtxtInner<'tcx> {
const_unification_storage: Default::default(),
int_unification_storage: Default::default(),
float_unification_storage: Default::default(),
effect_unification_storage: Default::default(),
region_constraint_storage: Some(Default::default()),
region_obligations: vec![],
opaque_type_storage: Default::default(),
@ -228,10 +222,6 @@ impl<'tcx> InferCtxtInner<'tcx> {
self.const_unification_storage.with_log(&mut self.undo_log)
}
fn effect_unification_table(&mut self) -> UnificationTable<'_, 'tcx, EffectVidKey<'tcx>> {
self.effect_unification_storage.with_log(&mut self.undo_log)
}
#[inline]
pub fn unwrap_region_constraints(&mut self) -> RegionConstraintCollector<'_, 'tcx> {
self.region_constraint_storage
@ -524,7 +514,6 @@ impl fmt::Display for FixupError {
),
Ty(_) => write!(f, "unconstrained type"),
Const(_) => write!(f, "unconstrained const value"),
Effect(_) => write!(f, "unconstrained effect value"),
}
}
}
@ -726,17 +715,6 @@ impl<'tcx> InferCtxt<'tcx> {
vars
}
pub fn unsolved_effects(&self) -> Vec<ty::Const<'tcx>> {
let mut inner = self.inner.borrow_mut();
let mut table = inner.effect_unification_table();
(0..table.len())
.map(|i| ty::EffectVid::from_usize(i))
.filter(|&vid| table.probe_value(vid).is_unknown())
.map(|v| ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(v)))
.collect()
}
#[instrument(skip(self), level = "debug")]
pub fn sub_regions(
&self,
@ -899,13 +877,6 @@ impl<'tcx> InferCtxt<'tcx> {
ty::Const::new_var(self.tcx, vid)
}
fn next_effect_var(&self) -> ty::Const<'tcx> {
let effect_vid =
self.inner.borrow_mut().effect_unification_table().new_key(EffectVarValue::Unknown).vid;
ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(effect_vid))
}
pub fn next_int_var(&self) -> Ty<'tcx> {
let next_int_var_id =
self.inner.borrow_mut().int_unification_table().new_key(ty::IntVarValue::Unknown);
@ -991,10 +962,7 @@ impl<'tcx> InferCtxt<'tcx> {
Ty::new_var(self.tcx, ty_var_id).into()
}
GenericParamDefKind::Const { is_host_effect, .. } => {
if is_host_effect {
return self.var_for_effect(param);
}
GenericParamDefKind::Const { .. } => {
let origin = ConstVariableOrigin { param_def_id: Some(param.def_id), span };
let const_var_id = self
.inner
@ -1007,16 +975,6 @@ impl<'tcx> InferCtxt<'tcx> {
}
}
pub fn var_for_effect(&self, param: &ty::GenericParamDef) -> GenericArg<'tcx> {
let ty = self
.tcx
.type_of(param.def_id)
.no_bound_vars()
.expect("const parameter types cannot be generic");
debug_assert_eq!(self.tcx.types.bool, ty);
self.next_effect_var().into()
}
/// Given a set of generics defined on a type or impl, returns the generic parameters mapping
/// each type/region parameter to a fresh inference variable.
pub fn fresh_args_for_item(&self, span: Span, def_id: DefId) -> GenericArgsRef<'tcx> {
@ -1142,13 +1100,6 @@ impl<'tcx> InferCtxt<'tcx> {
.probe_value(vid)
.known()
.unwrap_or(ct),
InferConst::EffectVar(vid) => self
.inner
.borrow_mut()
.effect_unification_table()
.probe_value(vid)
.known()
.unwrap_or(ct),
InferConst::Fresh(_) => ct,
},
ty::ConstKind::Param(_)
@ -1169,10 +1120,6 @@ impl<'tcx> InferCtxt<'tcx> {
self.inner.borrow_mut().const_unification_table().find(var).vid
}
pub fn root_effect_var(&self, var: ty::EffectVid) -> ty::EffectVid {
self.inner.borrow_mut().effect_unification_table().find(var).vid
}
/// Resolves an int var to a rigid int type, if it was constrained to one,
/// or else the root int var in the unification table.
pub fn opportunistic_resolve_int_var(&self, vid: ty::IntVid) -> Ty<'tcx> {
@ -1238,10 +1185,6 @@ impl<'tcx> InferCtxt<'tcx> {
}
}
pub fn probe_effect_var(&self, vid: EffectVid) -> Option<ty::Const<'tcx>> {
self.inner.borrow_mut().effect_unification_table().probe_value(vid).known()
}
/// Attempts to resolve all type/region/const variables in
/// `value`. Region inference must have been run already (e.g.,
/// by calling `resolve_regions_and_report_errors`). If some
@ -1511,14 +1454,6 @@ impl<'tcx> InferCtxt<'tcx> {
ConstVariableValue::Known { .. } => true,
}
}
TyOrConstInferVar::Effect(v) => {
// If `probe_value` returns `Some`, it never equals
// `ty::ConstKind::Infer(ty::InferConst::Effect(v))`.
//
// Not `inlined_probe_value(v)` because this call site is colder.
self.probe_effect_var(v).is_some()
}
}
}
@ -1545,8 +1480,6 @@ pub enum TyOrConstInferVar {
/// Equivalent to `ty::ConstKind::Infer(ty::InferConst::Var(_))`.
Const(ConstVid),
/// Equivalent to `ty::ConstKind::Infer(ty::InferConst::EffectVar(_))`.
Effect(EffectVid),
}
impl<'tcx> TyOrConstInferVar {
@ -1577,7 +1510,6 @@ impl<'tcx> TyOrConstInferVar {
fn maybe_from_const(ct: ty::Const<'tcx>) -> Option<Self> {
match ct.kind() {
ty::ConstKind::Infer(InferConst::Var(v)) => Some(TyOrConstInferVar::Const(v)),
ty::ConstKind::Infer(InferConst::EffectVar(v)) => Some(TyOrConstInferVar::Effect(v)),
_ => None,
}
}

View File

@ -24,19 +24,9 @@ pub fn explicit_outlives_bounds<'tcx>(
param_env
.caller_bounds()
.into_iter()
.map(ty::Clause::kind)
.filter_map(ty::Clause::as_region_outlives_clause)
.filter_map(ty::Binder::no_bound_vars)
.filter_map(move |kind| match kind {
ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(r_a, r_b)) => {
Some(OutlivesBound::RegionSubRegion(r_b, r_a))
}
ty::ClauseKind::Trait(_)
| ty::ClauseKind::TypeOutlives(_)
| ty::ClauseKind::Projection(_)
| ty::ClauseKind::ConstArgHasType(_, _)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(_) => None,
})
.map(|ty::OutlivesPredicate(r_a, r_b)| OutlivesBound::RegionSubRegion(r_b, r_a))
}
impl<'tcx> InferCtxt<'tcx> {

View File

@ -660,7 +660,6 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for Generalizer<'_, 'tcx> {
}
}
}
ty::ConstKind::Infer(InferConst::EffectVar(_)) => Ok(c),
// FIXME: Unevaluated constants are also not rigid, so the current
// approach of always relating them structurally is incomplete.
//

View File

@ -176,9 +176,6 @@ impl<'a, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for FullTypeResolver<'a, 'tcx> {
ty::ConstKind::Infer(InferConst::Fresh(_)) => {
bug!("Unexpected const in full const resolver: {:?}", c);
}
ty::ConstKind::Infer(InferConst::EffectVar(evid)) => {
return Err(FixupError { unresolved: super::TyOrConstInferVar::Effect(evid) });
}
_ => {}
}
c.try_super_fold_with(self)

View File

@ -4,7 +4,6 @@ use rustc_data_structures::{snapshot_vec as sv, unify as ut};
use rustc_middle::infer::unify_key::{ConstVariableValue, ConstVidKey};
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
use rustc_middle::ty::{self, ConstVid, FloatVid, IntVid, RegionVid, Ty, TyCtxt, TyVid};
use rustc_type_ir::EffectVid;
use rustc_type_ir::visit::TypeVisitableExt;
use tracing::instrument;
use ut::UnifyKey;
@ -129,7 +128,6 @@ struct SnapshotVarData {
int_vars: Range<IntVid>,
float_vars: Range<FloatVid>,
const_vars: (Range<ConstVid>, Vec<ConstVariableOrigin>),
effect_vars: Range<EffectVid>,
}
impl SnapshotVarData {
@ -148,30 +146,16 @@ impl SnapshotVarData {
&mut inner.const_unification_table(),
vars_pre_snapshot.const_var_len,
);
let effect_vars = vars_since_snapshot(
&inner.effect_unification_table(),
vars_pre_snapshot.effect_var_len,
);
let effect_vars = effect_vars.start.vid..effect_vars.end.vid;
SnapshotVarData { region_vars, type_vars, int_vars, float_vars, const_vars, effect_vars }
SnapshotVarData { region_vars, type_vars, int_vars, float_vars, const_vars }
}
fn is_empty(&self) -> bool {
let SnapshotVarData {
region_vars,
type_vars,
int_vars,
float_vars,
const_vars,
effect_vars,
} = self;
let SnapshotVarData { region_vars, type_vars, int_vars, float_vars, const_vars } = self;
region_vars.0.is_empty()
&& type_vars.0.is_empty()
&& int_vars.is_empty()
&& float_vars.is_empty()
&& const_vars.0.is_empty()
&& effect_vars.is_empty()
}
}
@ -258,13 +242,6 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for InferenceFudger<'a, 'tcx> {
ct
}
}
ty::InferConst::EffectVar(vid) => {
if self.snapshot_vars.effect_vars.contains(&vid) {
self.infcx.next_effect_var()
} else {
ct
}
}
ty::InferConst::Fresh(_) => {
unreachable!("unexpected fresh infcx var")
}

View File

@ -23,7 +23,6 @@ struct VariableLengths {
int_var_len: usize,
float_var_len: usize,
const_var_len: usize,
effect_var_len: usize,
}
impl<'tcx> InferCtxt<'tcx> {
@ -35,7 +34,6 @@ impl<'tcx> InferCtxt<'tcx> {
int_var_len: inner.int_unification_table().len(),
float_var_len: inner.float_unification_table().len(),
const_var_len: inner.const_unification_table().len(),
effect_var_len: inner.effect_unification_table().len(),
}
}

View File

@ -2,7 +2,7 @@ use std::marker::PhantomData;
use rustc_data_structures::undo_log::{Rollback, UndoLogs};
use rustc_data_structures::{snapshot_vec as sv, unify as ut};
use rustc_middle::infer::unify_key::{ConstVidKey, EffectVidKey, RegionVidKey};
use rustc_middle::infer::unify_key::{ConstVidKey, RegionVidKey};
use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey};
use tracing::debug;
@ -22,7 +22,6 @@ pub(crate) enum UndoLog<'tcx> {
ConstUnificationTable(sv::UndoLog<ut::Delegate<ConstVidKey<'tcx>>>),
IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>),
FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>),
EffectUnificationTable(sv::UndoLog<ut::Delegate<EffectVidKey<'tcx>>>),
RegionConstraintCollector(region_constraints::UndoLog<'tcx>),
RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>),
ProjectionCache(traits::UndoLog<'tcx>),
@ -50,7 +49,6 @@ impl_from! {
FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>),
ConstUnificationTable(sv::UndoLog<ut::Delegate<ConstVidKey<'tcx>>>),
EffectUnificationTable(sv::UndoLog<ut::Delegate<EffectVidKey<'tcx>>>),
RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>),
ProjectionCache(traits::UndoLog<'tcx>),
@ -65,7 +63,6 @@ impl<'tcx> Rollback<UndoLog<'tcx>> for InferCtxtInner<'tcx> {
UndoLog::ConstUnificationTable(undo) => self.const_unification_storage.reverse(undo),
UndoLog::IntUnificationTable(undo) => self.int_unification_storage.reverse(undo),
UndoLog::FloatUnificationTable(undo) => self.float_unification_storage.reverse(undo),
UndoLog::EffectUnificationTable(undo) => self.effect_unification_storage.reverse(undo),
UndoLog::RegionConstraintCollector(undo) => {
self.region_constraint_storage.as_mut().unwrap().reverse(undo)
}

View File

@ -1554,7 +1554,9 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints {
// Ignore bounds that a user can't type
| ClauseKind::WellFormed(..)
// FIXME(generic_const_exprs): `ConstEvaluatable` can be written
| ClauseKind::ConstEvaluatable(..) => continue,
| ClauseKind::ConstEvaluatable(..)
// Users don't write this directly, only via another trait ref.
| ty::ClauseKind::HostEffect(..) => continue,
};
if predicate.is_global() {
cx.emit_span_lint(TRIVIAL_BOUNDS, span, BuiltinTrivialBounds {

View File

@ -542,11 +542,7 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
}
fn check_generic_param(&mut self, cx: &LateContext<'_>, param: &hir::GenericParam<'_>) {
if let GenericParamKind::Const { is_host_effect, .. } = param.kind {
// `host` params are explicitly allowed to be lowercase.
if is_host_effect {
return;
}
if let GenericParamKind::Const { .. } = param.kind {
NonUpperCaseGlobals::check_upper_case(cx, "const parameter", &param.name.ident());
}
}

View File

@ -275,6 +275,8 @@ provide! { tcx, def_id, other, cdata,
impl_parent => { table }
defaultness => { table_direct }
constness => { table_direct }
const_conditions => { table }
implied_const_bounds => { table_defaulted_array }
coerce_unsized_info => {
Ok(cdata
.root
@ -330,7 +332,6 @@ provide! { tcx, def_id, other, cdata,
.process_decoded(tcx, || panic!("{def_id:?} does not have trait_impl_trait_tys")))
}
associated_type_for_effects => { table }
associated_types_for_impl_traits_in_associated_fn => { table_defaulted_array }
visibility => { cdata.get_visibility(def_id.index) }

View File

@ -1433,6 +1433,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
}
}
}
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));
}
@ -1456,10 +1459,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
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());
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));
@ -1479,9 +1485,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
for &def_id in associated_item_def_ids {
self.encode_info_for_assoc_item(def_id);
}
if let Some(assoc_def_id) = self.tcx.associated_type_for_effects(def_id) {
record!(self.tables.associated_type_for_effects[def_id] <- assoc_def_id);
}
}
if let DefKind::Closure | DefKind::SyntheticCoroutineBody = def_kind
&& let Some(coroutine_kind) = self.tcx.coroutine_kind(def_id)
@ -1652,6 +1655,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
if let ty::AssocKind::Type = item.kind {
self.encode_explicit_item_bounds(def_id);
self.encode_explicit_item_super_predicates(def_id);
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

@ -392,9 +392,9 @@ define_tables! {
inferred_outlives_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>,
explicit_super_predicates_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>,
explicit_implied_predicates_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>,
implied_const_bounds: Table<DefIndex, LazyArray<(ty::PolyTraitRef<'static>, Span)>>,
inherent_impls: Table<DefIndex, LazyArray<DefIndex>>,
associated_types_for_impl_traits_in_associated_fn: Table<DefIndex, LazyArray<DefId>>,
associated_type_for_effects: Table<DefIndex, Option<LazyValue<DefId>>>,
opt_rpitit_info: Table<DefIndex, Option<LazyValue<ty::ImplTraitInTraitData>>>,
is_effects_desugaring: Table<DefIndex, bool>,
unused_generic_params: Table<DefIndex, UnusedGenericParams>,
@ -436,6 +436,7 @@ define_tables! {
thir_abstract_const: Table<DefIndex, LazyValue<ty::EarlyBinder<'static, ty::Const<'static>>>>,
impl_parent: Table<DefIndex, RawDefId>,
constness: Table<DefIndex, hir::Constness>,
const_conditions: Table<DefIndex, LazyValue<ty::ConstConditions<'static>>>,
defaultness: Table<DefIndex, hir::Defaultness>,
// FIXME(eddyb) perhaps compute this on the fly if cheap enough?
coerce_unsized_info: Table<DefIndex, LazyValue<ty::adjustment::CoerceUnsizedInfo>>,

View File

@ -172,70 +172,3 @@ impl<'tcx> UnifyValue for ConstVariableValue<'tcx> {
}
}
}
/// values for the effect inference variable
#[derive(Clone, Copy, Debug)]
pub enum EffectVarValue<'tcx> {
Unknown,
Known(ty::Const<'tcx>),
}
impl<'tcx> EffectVarValue<'tcx> {
pub fn known(self) -> Option<ty::Const<'tcx>> {
match self {
EffectVarValue::Unknown => None,
EffectVarValue::Known(value) => Some(value),
}
}
pub fn is_unknown(self) -> bool {
match self {
EffectVarValue::Unknown => true,
EffectVarValue::Known(_) => false,
}
}
}
impl<'tcx> UnifyValue for EffectVarValue<'tcx> {
type Error = NoError;
fn unify_values(value1: &Self, value2: &Self) -> Result<Self, Self::Error> {
match (*value1, *value2) {
(EffectVarValue::Unknown, EffectVarValue::Unknown) => Ok(EffectVarValue::Unknown),
(EffectVarValue::Unknown, EffectVarValue::Known(val))
| (EffectVarValue::Known(val), EffectVarValue::Unknown) => {
Ok(EffectVarValue::Known(val))
}
(EffectVarValue::Known(_), EffectVarValue::Known(_)) => {
bug!("equating known inference variables: {value1:?} {value2:?}")
}
}
}
}
#[derive(PartialEq, Copy, Clone, Debug)]
pub struct EffectVidKey<'tcx> {
pub vid: ty::EffectVid,
pub phantom: PhantomData<ty::Const<'tcx>>,
}
impl<'tcx> From<ty::EffectVid> for EffectVidKey<'tcx> {
fn from(vid: ty::EffectVid) -> Self {
EffectVidKey { vid, phantom: PhantomData }
}
}
impl<'tcx> UnifyKey for EffectVidKey<'tcx> {
type Value = EffectVarValue<'tcx>;
#[inline]
fn index(&self) -> u32 {
self.vid.as_u32()
}
#[inline]
fn from_index(i: u32) -> Self {
EffectVidKey::from(ty::EffectVid::from_u32(i))
}
fn tag() -> &'static str {
"EffectVidKey"
}
}

View File

@ -370,6 +370,7 @@ tcx_lifetime! {
rustc_middle::ty::FnSig,
rustc_middle::ty::GenericArg,
rustc_middle::ty::GenericPredicates,
rustc_middle::ty::ConstConditions,
rustc_middle::ty::inhabitedness::InhabitedPredicate,
rustc_middle::ty::Instance,
rustc_middle::ty::InstanceKind,

View File

@ -683,6 +683,24 @@ rustc_queries! {
}
}
query const_conditions(
key: DefId
) -> ty::ConstConditions<'tcx> {
desc { |tcx| "computing the conditions for `{}` to be considered const",
tcx.def_path_str(key)
}
separate_provide_extern
}
query implied_const_bounds(
key: DefId
) -> ty::EarlyBinder<'tcx, &'tcx [(ty::PolyTraitRef<'tcx>, Span)]> {
desc { |tcx| "computing the implied `~const` bounds for `{}`",
tcx.def_path_str(key)
}
separate_provide_extern
}
/// To avoid cycles within the predicates of a single item we compute
/// per-type-parameter predicates for resolving `T::AssocTy`.
query type_param_predicates(
@ -854,12 +872,6 @@ rustc_queries! {
separate_provide_extern
}
query associated_type_for_effects(def_id: DefId) -> Option<DefId> {
desc { |tcx| "creating associated items for effects in `{}`", tcx.def_path_str(def_id) }
cache_on_disk_if { def_id.is_local() }
separate_provide_extern
}
/// Given an impl trait in trait `opaque_ty_def_id`, create and return the corresponding
/// associated item.
query associated_type_for_impl_trait_in_trait(opaque_ty_def_id: LocalDefId) -> LocalDefId {

View File

@ -386,6 +386,17 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> for [(ty::Claus
}
}
impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D>
for [(ty::PolyTraitRef<'tcx>, Span)]
{
fn decode(decoder: &mut D) -> &'tcx Self {
decoder
.interner()
.arena
.alloc_from_iter((0..decoder.read_usize()).map(|_| Decodable::decode(decoder)))
}
}
impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D>
for ty::List<ty::BoundVariableKind>
{

View File

@ -78,10 +78,10 @@ use crate::traits::solve::{
use crate::ty::predicate::ExistentialPredicateStableCmpExt as _;
use crate::ty::{
self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Clauses, Const, GenericArg, GenericArgs,
GenericArgsRef, GenericParamDefKind, ImplPolarity, List, ListWithCachedTypeInfo, ParamConst,
ParamTy, Pattern, PatternKind, PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind,
PredicatePolarity, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid,
Visibility,
GenericArgsRef, GenericParamDefKind, HostPolarity, ImplPolarity, List, ListWithCachedTypeInfo,
ParamConst, ParamTy, Pattern, PatternKind, PolyExistentialPredicate, PolyFnSig, Predicate,
PredicateKind, PredicatePolarity, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty,
TyKind, TyVid, Visibility,
};
#[allow(rustc::usage_of_ty_tykind)]
@ -383,6 +383,28 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
self.explicit_implied_predicates_of(def_id).map_bound(|preds| preds.into_iter().copied())
}
fn is_const_impl(self, def_id: DefId) -> bool {
self.is_conditionally_const(def_id)
}
fn const_conditions(
self,
def_id: DefId,
) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = ty::Binder<'tcx, ty::TraitRef<'tcx>>>> {
ty::EarlyBinder::bind(
self.const_conditions(def_id).instantiate_identity(self).into_iter().map(|(c, _)| c),
)
}
fn implied_const_bounds(
self,
def_id: DefId,
) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = ty::Binder<'tcx, ty::TraitRef<'tcx>>>> {
ty::EarlyBinder::bind(
self.implied_const_bounds(def_id).iter_identity_copied().map(|(c, _)| c),
)
}
fn has_target_features(self, def_id: DefId) -> bool {
!self.codegen_fn_attrs(def_id).target_features.is_empty()
}
@ -646,13 +668,6 @@ bidirectional_lang_item_map! {
Destruct,
DiscriminantKind,
DynMetadata,
EffectsCompat,
EffectsIntersection,
EffectsIntersectionOutput,
EffectsMaybe,
EffectsNoRuntime,
EffectsRuntime,
EffectsTyCompat,
Fn,
FnMut,
FnOnce,
@ -2196,7 +2211,7 @@ macro_rules! nop_slice_lift {
nop_slice_lift! {ty::ValTree<'a> => ty::ValTree<'tcx>}
TrivialLiftImpls! {
ImplPolarity, PredicatePolarity, Promoted
ImplPolarity, PredicatePolarity, Promoted, HostPolarity,
}
macro_rules! sty_debug_print {
@ -3125,6 +3140,7 @@ impl<'tcx> TyCtxt<'tcx> {
}
}
// 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

@ -592,9 +592,6 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IsSuggestableVisitor<'tcx> {
match c.kind() {
ConstKind::Infer(InferConst::Var(_)) if self.infer_suggestable => {}
// effect variables are always suggestable, because they are not visible
ConstKind::Infer(InferConst::EffectVar(_)) => {}
ConstKind::Infer(..)
| ConstKind::Bound(..)
| ConstKind::Placeholder(..)

View File

@ -265,6 +265,12 @@ impl FlagComputation {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) => {
self.add_args(trait_pred.trait_ref.args);
}
ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(ty::HostEffectPredicate {
trait_ref,
host: _,
})) => {
self.add_args(trait_ref.args);
}
ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(
a,
b,
@ -354,9 +360,7 @@ impl FlagComputation {
self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
match infer {
InferConst::Fresh(_) => self.add_flags(TypeFlags::HAS_CT_FRESH),
InferConst::Var(_) | InferConst::EffectVar(_) => {
self.add_flags(TypeFlags::HAS_CT_INFER)
}
InferConst::Var(_) => self.add_flags(TypeFlags::HAS_CT_INFER),
}
}
ty::ConstKind::Bound(debruijn, _) => {

View File

@ -501,12 +501,11 @@ impl<'tcx> GenericArgs<'tcx> {
#[inline]
pub fn non_erasable_generics(
&'tcx self,
tcx: TyCtxt<'tcx>,
def_id: DefId,
// FIXME(effects): Remove these
_tcx: TyCtxt<'tcx>,
_def_id: DefId,
) -> impl DoubleEndedIterator<Item = GenericArgKind<'tcx>> + 'tcx {
let generics = tcx.generics_of(def_id);
self.iter().enumerate().filter_map(|(i, k)| match k.unpack() {
_ if Some(i) == generics.host_effect_index => None,
self.iter().filter_map(|k| match k.unpack() {
ty::GenericArgKind::Lifetime(_) => None,
generic => Some(generic),
})

View File

@ -14,7 +14,7 @@ use crate::ty::{EarlyBinder, GenericArgsRef};
pub enum GenericParamDefKind {
Lifetime,
Type { has_default: bool, synthetic: bool },
Const { has_default: bool, is_host_effect: bool, synthetic: bool },
Const { has_default: bool, synthetic: bool },
}
impl GenericParamDefKind {
@ -81,10 +81,6 @@ impl GenericParamDef {
}
}
pub fn is_host_effect(&self) -> bool {
matches!(self.kind, GenericParamDefKind::Const { is_host_effect: true, .. })
}
pub fn default_value<'tcx>(
&self,
tcx: TyCtxt<'tcx>,
@ -133,9 +129,6 @@ pub struct Generics {
pub has_self: bool,
pub has_late_bound_regions: Option<Span>,
// The index of the host effect when instantiated. (i.e. might be index to parent args)
pub host_effect_index: Option<usize>,
}
impl<'tcx> rustc_type_ir::inherent::GenericsOf<TyCtxt<'tcx>> for &'tcx Generics {
@ -216,12 +209,10 @@ impl<'tcx> Generics {
pub fn own_requires_monomorphization(&self) -> bool {
for param in &self.own_params {
match param.kind {
GenericParamDefKind::Type { .. }
| GenericParamDefKind::Const { is_host_effect: false, .. } => {
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
return true;
}
GenericParamDefKind::Lifetime
| GenericParamDefKind::Const { is_host_effect: true, .. } => {}
GenericParamDefKind::Lifetime => {}
}
}
false
@ -300,8 +291,6 @@ impl<'tcx> Generics {
own_params.start = 1;
}
let verbose = tcx.sess.verbose_internals();
// Filter the default arguments.
//
// This currently uses structural equality instead
@ -316,8 +305,6 @@ impl<'tcx> Generics {
param.default_value(tcx).is_some_and(|default| {
default.instantiate(tcx, args) == args[param.index as usize]
})
// filter out trailing effect params, if we're not in `-Zverbose-internals`.
|| (!verbose && matches!(param.kind, GenericParamDefKind::Const { is_host_effect: true, .. }))
})
.count();
@ -435,3 +422,73 @@ impl<'tcx> GenericPredicates<'tcx> {
instantiated.spans.extend(self.predicates.iter().map(|(_, s)| s));
}
}
/// `~const` bounds for a given item. This is represented using a struct much like
/// `GenericPredicates`, where you can either choose to only instantiate the "own"
/// bounds or all of the bounds including those from the parent. This distinction
/// is necessary for code like `compare_method_predicate_entailment`.
#[derive(Copy, Clone, Default, Debug, TyEncodable, TyDecodable, HashStable)]
pub struct ConstConditions<'tcx> {
pub parent: Option<DefId>,
pub predicates: &'tcx [(ty::PolyTraitRef<'tcx>, Span)],
}
impl<'tcx> ConstConditions<'tcx> {
pub fn instantiate(
self,
tcx: TyCtxt<'tcx>,
args: GenericArgsRef<'tcx>,
) -> Vec<(ty::PolyTraitRef<'tcx>, Span)> {
let mut instantiated = vec![];
self.instantiate_into(tcx, &mut instantiated, args);
instantiated
}
pub fn instantiate_own(
self,
tcx: TyCtxt<'tcx>,
args: GenericArgsRef<'tcx>,
) -> impl Iterator<Item = (ty::PolyTraitRef<'tcx>, Span)> + DoubleEndedIterator + ExactSizeIterator
{
EarlyBinder::bind(self.predicates).iter_instantiated_copied(tcx, args)
}
pub fn instantiate_own_identity(
self,
) -> impl Iterator<Item = (ty::PolyTraitRef<'tcx>, Span)> + DoubleEndedIterator + ExactSizeIterator
{
EarlyBinder::bind(self.predicates).iter_identity_copied()
}
#[instrument(level = "debug", skip(self, tcx))]
fn instantiate_into(
self,
tcx: TyCtxt<'tcx>,
instantiated: &mut Vec<(ty::PolyTraitRef<'tcx>, Span)>,
args: GenericArgsRef<'tcx>,
) {
if let Some(def_id) = self.parent {
tcx.const_conditions(def_id).instantiate_into(tcx, instantiated, args);
}
instantiated.extend(
self.predicates.iter().map(|&(p, s)| (EarlyBinder::bind(p).instantiate(tcx, args), s)),
);
}
pub fn instantiate_identity(self, tcx: TyCtxt<'tcx>) -> Vec<(ty::PolyTraitRef<'tcx>, Span)> {
let mut instantiated = vec![];
self.instantiate_identity_into(tcx, &mut instantiated);
instantiated
}
fn instantiate_identity_into(
self,
tcx: TyCtxt<'tcx>,
instantiated: &mut Vec<(ty::PolyTraitRef<'tcx>, Span)>,
) {
if let Some(def_id) = self.parent {
tcx.const_conditions(def_id).instantiate_identity_into(tcx, instantiated);
}
instantiated.extend(self.predicates.iter().copied());
}
}

View File

@ -476,7 +476,6 @@ impl<'tcx> Instance<'tcx> {
pub fn mono(tcx: TyCtxt<'tcx>, def_id: DefId) -> Instance<'tcx> {
let args = GenericArgs::for_item(tcx, def_id, |param, _| match param.kind {
ty::GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(),
ty::GenericParamDefKind::Const { is_host_effect: true, .. } => tcx.consts.true_.into(),
ty::GenericParamDefKind::Type { .. } => {
bug!("Instance::mono: {:?} has type parameters", def_id)
}

View File

@ -84,12 +84,13 @@ pub use self::parameterized::ParameterizedOverTcx;
pub use self::pattern::{Pattern, PatternKind};
pub use self::predicate::{
AliasTerm, Clause, ClauseKind, CoercePredicate, ExistentialPredicate,
ExistentialPredicateStableCmpExt, ExistentialProjection, ExistentialTraitRef, NormalizesTo,
OutlivesPredicate, PolyCoercePredicate, PolyExistentialPredicate, PolyExistentialProjection,
PolyExistentialTraitRef, PolyProjectionPredicate, PolyRegionOutlivesPredicate,
PolySubtypePredicate, PolyTraitPredicate, PolyTraitRef, PolyTypeOutlivesPredicate, Predicate,
PredicateKind, ProjectionPredicate, RegionOutlivesPredicate, SubtypePredicate, ToPolyTraitRef,
TraitPredicate, TraitRef, TypeOutlivesPredicate,
ExistentialPredicateStableCmpExt, ExistentialProjection, ExistentialTraitRef,
HostEffectPredicate, NormalizesTo, OutlivesPredicate, PolyCoercePredicate,
PolyExistentialPredicate, PolyExistentialProjection, PolyExistentialTraitRef,
PolyProjectionPredicate, PolyRegionOutlivesPredicate, PolySubtypePredicate, PolyTraitPredicate,
PolyTraitRef, PolyTypeOutlivesPredicate, Predicate, PredicateKind, ProjectionPredicate,
RegionOutlivesPredicate, SubtypePredicate, ToPolyTraitRef, TraitPredicate, TraitRef,
TypeOutlivesPredicate,
};
pub use self::region::BoundRegionKind::*;
pub use self::region::{
@ -1998,10 +1999,75 @@ impl<'tcx> TyCtxt<'tcx> {
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

@ -132,6 +132,7 @@ parameterized_over_tcx! {
ty::Ty,
ty::FnSig,
ty::GenericPredicates,
ty::ConstConditions,
ty::TraitRef,
ty::Const,
ty::Predicate,

View File

@ -19,6 +19,7 @@ pub type ExistentialPredicate<'tcx> = ir::ExistentialPredicate<TyCtxt<'tcx>>;
pub type ExistentialTraitRef<'tcx> = ir::ExistentialTraitRef<TyCtxt<'tcx>>;
pub type ExistentialProjection<'tcx> = ir::ExistentialProjection<TyCtxt<'tcx>>;
pub type TraitPredicate<'tcx> = ir::TraitPredicate<TyCtxt<'tcx>>;
pub type HostEffectPredicate<'tcx> = ir::HostEffectPredicate<TyCtxt<'tcx>>;
pub type ClauseKind<'tcx> = ir::ClauseKind<TyCtxt<'tcx>>;
pub type PredicateKind<'tcx> = ir::PredicateKind<TyCtxt<'tcx>>;
pub type NormalizesTo<'tcx> = ir::NormalizesTo<TyCtxt<'tcx>>;
@ -143,6 +144,7 @@ impl<'tcx> Predicate<'tcx> {
| PredicateKind::AliasRelate(..)
| PredicateKind::NormalizesTo(..) => false,
PredicateKind::Clause(ClauseKind::Trait(_))
| PredicateKind::Clause(ClauseKind::HostEffect(..))
| PredicateKind::Clause(ClauseKind::RegionOutlives(_))
| PredicateKind::Clause(ClauseKind::TypeOutlives(_))
| PredicateKind::Clause(ClauseKind::Projection(_))
@ -644,6 +646,7 @@ impl<'tcx> Predicate<'tcx> {
match predicate.skip_binder() {
PredicateKind::Clause(ClauseKind::Trait(t)) => Some(predicate.rebind(t)),
PredicateKind::Clause(ClauseKind::Projection(..))
| PredicateKind::Clause(ClauseKind::HostEffect(..))
| PredicateKind::Clause(ClauseKind::ConstArgHasType(..))
| PredicateKind::NormalizesTo(..)
| PredicateKind::AliasRelate(..)
@ -664,6 +667,7 @@ impl<'tcx> Predicate<'tcx> {
match predicate.skip_binder() {
PredicateKind::Clause(ClauseKind::Projection(t)) => Some(predicate.rebind(t)),
PredicateKind::Clause(ClauseKind::Trait(..))
| PredicateKind::Clause(ClauseKind::HostEffect(..))
| PredicateKind::Clause(ClauseKind::ConstArgHasType(..))
| PredicateKind::NormalizesTo(..)
| PredicateKind::AliasRelate(..)

View File

@ -3075,6 +3075,15 @@ define_print! {
p!(print(self.trait_ref.print_trait_sugared()))
}
ty::HostEffectPredicate<'tcx> {
let constness = match self.host {
ty::HostPolarity::Const => { "const" }
ty::HostPolarity::Maybe => { "~const" }
};
p!(print(self.trait_ref.self_ty()), ": {constness} ");
p!(print(self.trait_ref.print_trait_sugared()))
}
ty::TypeAndMut<'tcx> {
p!(write("{}", self.mutbl.prefix_str()), print(self.ty))
}
@ -3087,6 +3096,7 @@ define_print! {
ty::ClauseKind::RegionOutlives(predicate) => p!(print(predicate)),
ty::ClauseKind::TypeOutlives(predicate) => p!(print(predicate)),
ty::ClauseKind::Projection(predicate) => p!(print(predicate)),
ty::ClauseKind::HostEffect(predicate) => p!(print(predicate)),
ty::ClauseKind::ConstArgHasType(ct, ty) => {
p!("the constant `", print(ct), "` has type `", print(ty), "`")
},

View File

@ -17,10 +17,10 @@ use rustc_span::sym;
use rustc_target::abi::{Float, Integer, IntegerType, Size};
use rustc_target::spec::abi::Abi;
use smallvec::{SmallVec, smallvec};
use tracing::{debug, instrument, trace};
use tracing::{debug, instrument};
use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use crate::query::{IntoQueryParam, Providers};
use crate::query::Providers;
use crate::ty::layout::{FloatExt, IntegerExt};
use crate::ty::{
self, Asyncness, FallibleTypeFolder, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeFoldable,
@ -865,48 +865,6 @@ impl<'tcx> TyCtxt<'tcx> {
|| self.extern_crate(key).is_some_and(|e| e.is_direct())
}
/// Whether the item has a host effect param. This is different from `TyCtxt::is_const`,
/// because the item must also be "maybe const", and the crate where the item is
/// defined must also have the effects feature enabled.
pub fn has_host_param(self, def_id: impl IntoQueryParam<DefId>) -> bool {
self.generics_of(def_id).host_effect_index.is_some()
}
pub fn expected_host_effect_param_for_body(self, def_id: impl Into<DefId>) -> ty::Const<'tcx> {
let def_id = def_id.into();
// FIXME(effects): This is suspicious and should probably not be done,
// especially now that we enforce host effects and then properly handle
// effect vars during fallback.
let mut host_always_on = !self.features().effects()
|| self.sess.opts.unstable_opts.unleash_the_miri_inside_of_you;
// Compute the constness required by the context.
let const_context = self.hir().body_const_context(def_id);
let kind = self.def_kind(def_id);
debug_assert_ne!(kind, DefKind::ConstParam);
if self.has_attr(def_id, sym::rustc_do_not_const_check) {
trace!("do not const check this context");
host_always_on = true;
}
match const_context {
_ if host_always_on => self.consts.true_,
Some(hir::ConstContext::Static(_) | hir::ConstContext::Const { .. }) => {
self.consts.false_
}
Some(hir::ConstContext::ConstFn) => {
let host_idx = self
.generics_of(def_id)
.host_effect_index
.expect("ConstContext::Maybe must have host effect param");
ty::GenericArgs::identity_for_item(self, def_id).const_at(host_idx)
}
None => self.consts.true_,
}
}
/// Expand any [weak alias types][weak] contained within the given `value`.
///
/// This should be used over other normalization routines in situations where

View File

@ -1522,7 +1522,6 @@ fn create_mono_items_for_default_impls<'tcx>(
// it, to validate whether or not the impl is legal to instantiate at all.
let only_region_params = |param: &ty::GenericParamDef, _: &_| match param.kind {
GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(),
GenericParamDefKind::Const { is_host_effect: true, .. } => tcx.consts.true_.into(),
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
unreachable!(
"`own_requires_monomorphization` check means that \

View File

@ -431,7 +431,6 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
);
CanonicalVarKind::Const(self.delegate.universe_of_ct(vid).unwrap())
}
ty::InferConst::EffectVar(_) => CanonicalVarKind::Effect,
ty::InferConst::Fresh(_) => todo!(),
},
ty::ConstKind::Placeholder(placeholder) => match self.canonicalize_mode {

View File

@ -76,9 +76,6 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for EagerResolv
resolved
}
}
ty::ConstKind::Infer(ty::InferConst::EffectVar(vid)) => {
self.delegate.opportunistic_resolve_effect_var(vid)
}
_ => {
if c.has_infer() {
c.super_fold_with(self)

View File

@ -270,11 +270,6 @@ where
ecx: &mut EvalCtxt<'_, D>,
goal: Goal<I, Self>,
) -> Vec<Candidate<I>>;
fn consider_builtin_effects_intersection_candidate(
ecx: &mut EvalCtxt<'_, D>,
goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution>;
}
impl<D, I> EvalCtxt<'_, D>
@ -481,9 +476,6 @@ where
Some(TraitSolverLangItem::TransmuteTrait) => {
G::consider_builtin_transmute_candidate(self, goal)
}
Some(TraitSolverLangItem::EffectsIntersection) => {
G::consider_builtin_effects_intersection_candidate(self, goal)
}
_ => Err(NoSolution),
}
};

View File

@ -0,0 +1,296 @@
//! Dealing with host effect goals, i.e. enforcing the constness in
//! `T: const Trait` or `T: ~const Trait`.
use rustc_type_ir::fast_reject::DeepRejectCtxt;
use rustc_type_ir::inherent::*;
use rustc_type_ir::{self as ty, Interner};
use tracing::instrument;
use super::assembly::Candidate;
use crate::delegate::SolverDelegate;
use crate::solve::assembly::{self};
use crate::solve::{
BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, NoSolution,
QueryResult,
};
impl<D, I> assembly::GoalKind<D> for ty::HostEffectPredicate<I>
where
D: SolverDelegate<Interner = I>,
I: Interner,
{
fn self_ty(self) -> I::Ty {
self.self_ty()
}
fn trait_ref(self, _: I) -> ty::TraitRef<I> {
self.trait_ref
}
fn with_self_ty(self, cx: I, self_ty: I::Ty) -> Self {
self.with_self_ty(cx, self_ty)
}
fn trait_def_id(self, _: I) -> I::DefId {
self.def_id()
}
fn probe_and_match_goal_against_assumption(
ecx: &mut EvalCtxt<'_, D>,
source: rustc_type_ir::solve::CandidateSource<I>,
goal: Goal<I, Self>,
assumption: <I as Interner>::Clause,
then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
) -> Result<Candidate<I>, NoSolution> {
if let Some(host_clause) = assumption.as_host_effect_clause() {
if host_clause.def_id() == goal.predicate.def_id()
&& host_clause.host().satisfies(goal.predicate.host)
{
if !DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
goal.predicate.trait_ref.args,
host_clause.skip_binder().trait_ref.args,
) {
return Err(NoSolution);
}
ecx.probe_trait_candidate(source).enter(|ecx| {
let assumption_trait_pred = ecx.instantiate_binder_with_infer(host_clause);
ecx.eq(
goal.param_env,
goal.predicate.trait_ref,
assumption_trait_pred.trait_ref,
)?;
then(ecx)
})
} else {
Err(NoSolution)
}
} else {
Err(NoSolution)
}
}
fn consider_impl_candidate(
ecx: &mut EvalCtxt<'_, D>,
goal: Goal<I, Self>,
impl_def_id: <I as Interner>::DefId,
) -> Result<Candidate<I>, NoSolution> {
let cx = ecx.cx();
let impl_trait_ref = cx.impl_trait_ref(impl_def_id);
if !DeepRejectCtxt::relate_rigid_infer(ecx.cx())
.args_may_unify(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args)
{
return Err(NoSolution);
}
let impl_polarity = cx.impl_polarity(impl_def_id);
match impl_polarity {
ty::ImplPolarity::Negative => return Err(NoSolution),
ty::ImplPolarity::Reservation => {
unimplemented!("reservation impl for const trait: {:?}", goal)
}
ty::ImplPolarity::Positive => {}
};
if !cx.is_const_impl(impl_def_id) {
return Err(NoSolution);
}
ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| {
let impl_args = ecx.fresh_args_for_item(impl_def_id);
ecx.record_impl_args(impl_args);
let impl_trait_ref = impl_trait_ref.instantiate(cx, impl_args);
ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?;
let where_clause_bounds = cx
.predicates_of(impl_def_id)
.iter_instantiated(cx, impl_args)
.map(|pred| goal.with(cx, pred));
ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds);
// For this impl to be `const`, we need to check its `~const` bounds too.
let const_conditions = cx
.const_conditions(impl_def_id)
.iter_instantiated(cx, impl_args)
.map(|bound_trait_ref| {
goal.with(cx, bound_trait_ref.to_host_effect_clause(cx, goal.predicate.host))
});
ecx.add_goals(GoalSource::ImplWhereBound, const_conditions);
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
}
fn consider_error_guaranteed_candidate(
ecx: &mut EvalCtxt<'_, D>,
_guar: <I as Interner>::ErrorGuaranteed,
) -> Result<Candidate<I>, NoSolution> {
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
}
fn consider_auto_trait_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("auto traits are never const")
}
fn consider_trait_alias_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("trait aliases are never const")
}
fn consider_builtin_sized_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("Sized is never const")
}
fn consider_builtin_copy_clone_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
todo!("Copy/Clone is not yet const")
}
fn consider_builtin_pointer_like_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("PointerLike is not const")
}
fn consider_builtin_fn_ptr_trait_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
todo!("Fn* are not yet const")
}
fn consider_builtin_fn_trait_candidates(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
_kind: rustc_type_ir::ClosureKind,
) -> Result<Candidate<I>, NoSolution> {
todo!("Fn* are not yet const")
}
fn consider_builtin_async_fn_trait_candidates(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
_kind: rustc_type_ir::ClosureKind,
) -> Result<Candidate<I>, NoSolution> {
todo!("AsyncFn* are not yet const")
}
fn consider_builtin_async_fn_kind_helper_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("AsyncFnKindHelper is not const")
}
fn consider_builtin_tuple_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("Tuple trait is not const")
}
fn consider_builtin_pointee_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("Pointee is not const")
}
fn consider_builtin_future_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("Future is not const")
}
fn consider_builtin_iterator_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
todo!("Iterator is not yet const")
}
fn consider_builtin_fused_iterator_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("FusedIterator is not const")
}
fn consider_builtin_async_iterator_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("AsyncIterator is not const")
}
fn consider_builtin_coroutine_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("Coroutine is not const")
}
fn consider_builtin_discriminant_kind_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("DiscriminantKind is not const")
}
fn consider_builtin_async_destruct_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("AsyncDestruct is not const")
}
fn consider_builtin_destruct_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("Destruct is not const")
}
fn consider_builtin_transmute_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("TransmuteFrom is not const")
}
fn consider_structural_builtin_unsize_candidates(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Vec<Candidate<I>> {
unreachable!("Unsize is not const")
}
}
impl<D, I> EvalCtxt<'_, D>
where
D: SolverDelegate<Interner = I>,
I: Interner,
{
#[instrument(level = "trace", skip(self))]
pub(super) fn compute_host_effect_goal(
&mut self,
goal: Goal<I, ty::HostEffectPredicate<I>>,
) -> QueryResult<I> {
let candidates = self.assemble_and_evaluate_candidates(goal);
self.merge_candidates(candidates)
}
}

View File

@ -443,6 +443,9 @@ where
ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => {
self.compute_trait_goal(Goal { param_env, predicate })
}
ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(predicate)) => {
self.compute_host_effect_goal(Goal { param_env, predicate })
}
ty::PredicateKind::Clause(ty::ClauseKind::Projection(predicate)) => {
self.compute_projection_goal(Goal { param_env, predicate })
}

View File

@ -13,6 +13,7 @@
mod alias_relate;
mod assembly;
mod effect_goals;
mod eval_ctxt;
pub mod inspect;
mod normalizes_to;
@ -182,12 +183,6 @@ where
let (ct, ty) = goal.predicate;
let ct_ty = match ct.kind() {
// FIXME: Ignore effect vars because canonicalization doesn't handle them correctly
// and if we stall on the var then we wind up creating ambiguity errors in a probe
// for this goal which contains an effect var. Which then ends up ICEing.
ty::ConstKind::Infer(ty::InferConst::EffectVar(_)) => {
return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
}
ty::ConstKind::Infer(_) => {
return self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
}

View File

@ -911,68 +911,6 @@ where
) -> Result<Candidate<I>, NoSolution> {
panic!("`TransmuteFrom` does not have an associated type: {:?}", goal)
}
fn consider_builtin_effects_intersection_candidate(
ecx: &mut EvalCtxt<'_, D>,
goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
let ty::Tuple(types) = goal.predicate.self_ty().kind() else {
return Err(NoSolution);
};
let cx = ecx.cx();
let mut first_non_maybe = None;
let mut non_maybe_count = 0;
for ty in types.iter() {
if !matches!(ty::EffectKind::try_from_ty(cx, ty), Some(ty::EffectKind::Maybe)) {
first_non_maybe.get_or_insert(ty);
non_maybe_count += 1;
}
}
match non_maybe_count {
0 => {
let ty = ty::EffectKind::Maybe.to_ty(cx);
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
ecx.instantiate_normalizes_to_term(goal, ty.into());
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
}
1 => {
let ty = first_non_maybe.unwrap();
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
ecx.instantiate_normalizes_to_term(goal, ty.into());
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
}
_ => {
let mut min = ty::EffectKind::Maybe;
for ty in types.iter() {
// We can't find the intersection if the types used are generic.
//
// FIXME(effects): do we want to look at where clauses to get some
// clue for the case where generic types are being used?
let Some(kind) = ty::EffectKind::try_from_ty(cx, ty) else {
return Err(NoSolution);
};
let Some(result) = ty::EffectKind::intersection(min, kind) else {
return Err(NoSolution);
};
min = result;
}
let ty = min.to_ty(cx);
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
ecx.instantiate_normalizes_to_term(goal, ty.into());
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
}
}
}
}
impl<D, I> EvalCtxt<'_, D>

View File

@ -719,47 +719,6 @@ where
}
})
}
fn consider_builtin_effects_intersection_candidate(
ecx: &mut EvalCtxt<'_, D>,
goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
return Err(NoSolution);
}
let ty::Tuple(types) = goal.predicate.self_ty().kind() else {
return Err(NoSolution);
};
let cx = ecx.cx();
let maybe_count = types
.iter()
.filter_map(|ty| ty::EffectKind::try_from_ty(cx, ty))
.filter(|&ty| ty == ty::EffectKind::Maybe)
.count();
// Don't do concrete type check unless there are more than one type that will influence the result.
// This would allow `(Maybe, T): Min` pass even if we know nothing about `T`.
if types.len() - maybe_count > 1 {
let mut min = ty::EffectKind::Maybe;
for ty in types.iter() {
let Some(kind) = ty::EffectKind::try_from_ty(ecx.cx(), ty) else {
return Err(NoSolution);
};
let Some(result) = ty::EffectKind::intersection(min, kind) else {
return Err(NoSolution);
};
min = result;
}
}
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
}
}
impl<D, I> EvalCtxt<'_, D>

View File

@ -137,6 +137,10 @@ where
ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, polarity: _ }) => {
self.visit_trait(trait_ref)
}
ty::ClauseKind::HostEffect(pred) => {
try_visit!(self.visit_trait(pred.trait_ref));
pred.host.visit_with(self)
}
ty::ClauseKind::Projection(ty::ProjectionPredicate {
projection_term: projection_ty,
term,

View File

@ -588,7 +588,6 @@ impl<'tcx> Stable<'tcx> for ty::Generics {
.has_late_bound_regions
.as_ref()
.map(|late_bound_regions| late_bound_regions.stable(tables)),
host_effect_index: self.host_effect_index,
}
}
}
@ -603,7 +602,7 @@ impl<'tcx> Stable<'tcx> for rustc_middle::ty::GenericParamDefKind {
ty::GenericParamDefKind::Type { has_default, synthetic } => {
GenericParamDefKind::Type { has_default: *has_default, synthetic: *synthetic }
}
ty::GenericParamDefKind::Const { has_default, is_host_effect: _, synthetic: _ } => {
ty::GenericParamDefKind::Const { has_default, synthetic: _ } => {
GenericParamDefKind::Const { has_default: *has_default }
}
}
@ -690,6 +689,9 @@ impl<'tcx> Stable<'tcx> for ty::ClauseKind<'tcx> {
ClauseKind::ConstEvaluatable(const_) => {
stable_mir::ty::ClauseKind::ConstEvaluatable(const_.stable(tables))
}
ClauseKind::HostEffect(..) => {
todo!()
}
}
}
}

View File

@ -195,14 +195,6 @@ symbols! {
Display,
DoubleEndedIterator,
Duration,
EffectsCompat,
EffectsIntersection,
EffectsIntersectionOutput,
EffectsMaybe,
EffectsNoRuntime,
EffectsRuntime,
EffectsTyCompat,
Effects__,
Encodable,
Encoder,
Enumerate,
@ -1737,7 +1729,6 @@ symbols! {
rustc_reallocator,
rustc_regions,
rustc_reservation_impl,
rustc_runtime,
rustc_safe_intrinsic,
rustc_serialize,
rustc_skip_during_method_dispatch,

View File

@ -156,8 +156,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
(leaf_trait_predicate, &obligation)
};
let (main_trait_predicate, leaf_trait_predicate, predicate_constness) = self.get_effects_trait_pred_override(main_trait_predicate, leaf_trait_predicate, span);
let main_trait_ref = main_trait_predicate.to_poly_trait_ref();
let leaf_trait_ref = leaf_trait_predicate.to_poly_trait_ref();
@ -228,7 +226,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
let err_msg = self.get_standard_error_message(
main_trait_predicate,
message,
predicate_constness,
None,
append_const_msg,
post_message,
);
@ -289,13 +287,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
);
}
if tcx.is_lang_item(leaf_trait_ref.def_id(), LangItem::Drop)
&& matches!(predicate_constness, Some(ty::BoundConstness::ConstIfConst | ty::BoundConstness::Const))
{
err.note("`~const Drop` was renamed to `~const Destruct`");
err.note("See <https://github.com/rust-lang/rust/pull/94901> for more details");
}
let explanation = get_explanation_based_on_obligation(
self.tcx,
&obligation,
@ -541,6 +532,29 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
err
}
ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(predicate)) => {
// FIXME(effects): We should recompute the predicate with `~const`
// if it's `const`, and if it holds, explain that this bound only
// *conditionally* holds. If that fails, we should also do selection
// to drill this down to an impl or built-in source, so we can
// point at it and explain that while the trait *is* implemented,
// that implementation is not const.
let err_msg = self.get_standard_error_message(
bound_predicate.rebind(ty::TraitPredicate {
trait_ref: predicate.trait_ref,
polarity: ty::PredicatePolarity::Positive,
}),
None,
Some(match predicate.host {
ty::HostPolarity::Maybe => ty::BoundConstness::ConstIfConst,
ty::HostPolarity::Const => ty::BoundConstness::Const,
}),
None,
String::new(),
);
struct_span_code_err!(self.dcx(), span, E0277, "{}", err_msg)
}
ty::PredicateKind::Subtype(predicate) => {
// Errors for Subtype predicates show up as
// `FulfillmentErrorCode::SubtypeError`,
@ -2374,52 +2388,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
})
}
/// For effects predicates such as `<u32 as Add>::Effects: Compat<host>`, pretend that the
/// predicate that failed was `u32: Add`. Return the constness of such predicate to later
/// print as `u32: ~const Add`.
fn get_effects_trait_pred_override(
&self,
p: ty::PolyTraitPredicate<'tcx>,
leaf: ty::PolyTraitPredicate<'tcx>,
span: Span,
) -> (ty::PolyTraitPredicate<'tcx>, ty::PolyTraitPredicate<'tcx>, Option<ty::BoundConstness>)
{
let trait_ref = p.to_poly_trait_ref();
if !self.tcx.is_lang_item(trait_ref.def_id(), LangItem::EffectsCompat) {
return (p, leaf, None);
}
let Some(ty::Alias(ty::AliasTyKind::Projection, projection)) =
trait_ref.self_ty().no_bound_vars().map(Ty::kind)
else {
return (p, leaf, None);
};
let constness = trait_ref.skip_binder().args.const_at(1);
let constness = if constness == self.tcx.consts.true_ || constness.is_ct_infer() {
None
} else if constness == self.tcx.consts.false_ {
Some(ty::BoundConstness::Const)
} else if matches!(constness.kind(), ty::ConstKind::Param(_)) {
Some(ty::BoundConstness::ConstIfConst)
} else {
self.dcx().span_bug(span, format!("Unknown constness argument: {constness:?}"));
};
let new_pred = p.map_bound(|mut trait_pred| {
trait_pred.trait_ref = projection.trait_ref(self.tcx);
trait_pred
});
let new_leaf = leaf.map_bound(|mut trait_pred| {
trait_pred.trait_ref = projection.trait_ref(self.tcx);
trait_pred
});
(new_pred, new_leaf, constness)
}
fn add_tuple_trait_message(
&self,
obligation_cause_code: &ObligationCauseCode<'tcx>,

View File

@ -806,7 +806,8 @@ impl<'tcx> AutoTraitFinder<'tcx> {
| ty::PredicateKind::Subtype(..)
// FIXME(generic_const_exprs): you can absolutely add this as a where clauses
| ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
| ty::PredicateKind::Coerce(..) => {}
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) => {}
ty::PredicateKind::Ambiguous => return false,
};
}

View File

@ -245,6 +245,7 @@ fn predicate_references_self<'tcx>(
| ty::ClauseKind::RegionOutlives(..)
// FIXME(generic_const_exprs): this can mention `Self`
| ty::ClauseKind::ConstEvaluatable(..)
| ty::ClauseKind::HostEffect(..)
=> None,
}
}
@ -284,7 +285,8 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
| ty::ClauseKind::Projection(_)
| ty::ClauseKind::ConstArgHasType(_, _)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(_) => false,
| ty::ClauseKind::ConstEvaluatable(_)
| ty::ClauseKind::HostEffect(..) => false,
})
}

View File

@ -372,7 +372,11 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
| ty::PredicateKind::Subtype(_)
| ty::PredicateKind::Coerce(_)
| ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..) => {
| ty::PredicateKind::ConstEquate(..)
// FIXME(effects): We may need to do this using the higher-ranked
// pred instead of just instantiating it with placeholders b/c of
// higher-ranked implied bound issues in the old solver.
| ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) => {
let pred = ty::Binder::dummy(infcx.enter_forall_and_leak_universe(binder));
let mut obligations = PredicateObligations::with_capacity(1);
obligations.push(obligation.with(infcx.tcx, pred));
@ -398,6 +402,10 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
)
}
ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) => {
ProcessResult::Changed(Default::default())
}
ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(data)) => {
if infcx.considering_regions {
infcx.region_outlives_predicate(&obligation.cause, Binder::dummy(data));
@ -450,7 +458,6 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
ty::ConstKind::Infer(var) => {
let var = match var {
ty::InferConst::Var(vid) => TyOrConstInferVar::Const(vid),
ty::InferConst::EffectVar(vid) => TyOrConstInferVar::Effect(vid),
ty::InferConst::Fresh(_) => {
bug!("encountered fresh const in fulfill")
}

View File

@ -96,6 +96,7 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>(
// FIXME(const_generics): Make sure that `<'a, 'b, const N: &'a &'b u32>` is sound
// if we ever support that
ty::PredicateKind::Clause(ty::ClauseKind::Trait(..))
| ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..))
| ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
@ -200,6 +201,7 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>(
// FIXME(const_generics): Make sure that `<'a, 'b, const N: &'a &'b u32>` is sound
// if we ever support that
ty::PredicateKind::Clause(ty::ClauseKind::Trait(..))
| ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..))
| ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)

View File

@ -546,7 +546,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
// Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396).
ty::FnDef(def_id, _args) => {
ty::FnDef(def_id, _) => {
let tcx = self.tcx();
if tcx.fn_sig(def_id).skip_binder().is_fn_trait_compatible()
&& tcx.codegen_fn_attrs(def_id).target_features.is_empty()

View File

@ -645,6 +645,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
self.evaluate_trait_predicate_recursively(previous_stack, obligation)
}
ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) => {
// FIXME(effects): It should be relatively straightforward to implement
// old trait solver support for `HostEffect` bounds; or at least basic
// support for them.
todo!()
}
ty::PredicateKind::Subtype(p) => {
let p = bound_predicate.rebind(p);
// Does this code ever run?
@ -1821,8 +1828,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|cand: ty::PolyTraitPredicate<'tcx>| cand.is_global() && !cand.has_bound_vars();
// (*) Prefer `BuiltinCandidate { has_nested: false }`, `PointeeCandidate`,
// `DiscriminantKindCandidate`, `ConstDestructCandidate`
// to anything else.
// or `DiscriminantKindCandidate` to anything else.
//
// This is a fix for #53123 and prevents winnowing from accidentally extending the
// lifetime of a variable.

View File

@ -170,6 +170,10 @@ pub fn clause_obligations<'tcx>(
ty::ClauseKind::Trait(t) => {
wf.compute_trait_pred(t, Elaborate::None);
}
ty::ClauseKind::HostEffect(..) => {
// Technically the well-formedness of this predicate is implied by
// the corresponding trait predicate it should've been generated beside.
}
ty::ClauseKind::RegionOutlives(..) => {}
ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) => {
wf.compute(ty.into());
@ -1021,6 +1025,7 @@ pub(crate) fn required_region_bounds<'tcx>(
}
}
ty::ClauseKind::Trait(_)
| ty::ClauseKind::HostEffect(..)
| ty::ClauseKind::RegionOutlives(_)
| ty::ClauseKind::Projection(_)
| ty::ClauseKind::ConstArgHasType(_, _)

View File

@ -55,6 +55,7 @@ fn not_outlives_predicate(p: ty::Predicate<'_>) -> bool {
| ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..)) => false,
ty::PredicateKind::Clause(ty::ClauseKind::Trait(..))
| ty::PredicateKind::Clause(ty::ClauseKind::Projection(..))
| ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..))
| ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
| ty::PredicateKind::NormalizesTo(..)
| ty::PredicateKind::AliasRelate(..)

View File

@ -4,9 +4,8 @@ use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
use rustc_hir::intravisit::{self, Visitor};
use rustc_middle::query::Providers;
use rustc_middle::ty::{self, ImplTraitInTraitData, Ty, TyCtxt};
use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_span::sym;
use rustc_span::symbol::kw;
pub(crate) fn provide(providers: &mut Providers) {
@ -15,7 +14,6 @@ pub(crate) fn provide(providers: &mut Providers) {
associated_item_def_ids,
associated_items,
associated_types_for_impl_traits_in_associated_fn,
associated_type_for_effects,
associated_type_for_impl_trait_in_trait,
impl_item_implementor_ids,
..*providers
@ -46,8 +44,7 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &[DefId] {
)
})
.copied(),
)
.chain(tcx.associated_type_for_effects(def_id)),
),
)
}
hir::ItemKind::Impl(impl_) => {
@ -73,8 +70,7 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &[DefId] {
)
})
.copied()
}))
.chain(tcx.associated_type_for_effects(def_id)),
})),
)
}
_ => span_bug!(item.span, "associated_item_def_ids: not impl or trait"),
@ -171,134 +167,6 @@ fn associated_item_from_impl_item_ref(impl_item_ref: &hir::ImplItemRef) -> ty::A
}
}
/// Given an `def_id` of a trait or a trait impl:
///
/// If `def_id` is a trait that has `#[const_trait]`, then it synthesizes
/// a new def id corresponding to a new associated type for the effects.
///
/// If `def_id` is an impl, then synthesize the associated type according
/// to the constness of the impl.
fn associated_type_for_effects(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<DefId> {
// don't synthesize the associated type even if the user has written `const_trait`
// if the effects feature is disabled.
if !tcx.features().effects() {
return None;
}
let (feed, parent_did) = match tcx.def_kind(def_id) {
DefKind::Trait => {
let trait_def_id = def_id;
let attr = tcx.get_attr(def_id, sym::const_trait)?;
let span = attr.span;
let trait_assoc_ty = tcx.at(span).create_def(trait_def_id, kw::Empty, DefKind::AssocTy);
let local_def_id = trait_assoc_ty.def_id();
let def_id = local_def_id.to_def_id();
// Copy span of the attribute.
trait_assoc_ty.def_ident_span(Some(span));
trait_assoc_ty.associated_item(ty::AssocItem {
name: kw::Empty,
kind: ty::AssocKind::Type,
def_id,
trait_item_def_id: None,
container: ty::TraitContainer,
fn_has_self_parameter: false,
opt_rpitit_info: None,
is_effects_desugaring: true,
});
// No default type
trait_assoc_ty.defaultness(hir::Defaultness::Default { has_value: false });
trait_assoc_ty.is_type_alias_impl_trait(false);
(trait_assoc_ty, trait_def_id)
}
DefKind::Impl { .. } => {
let impl_def_id = def_id;
let trait_id = tcx.trait_id_of_impl(def_id.to_def_id())?;
// first get the DefId of the assoc type on the trait, if there is not,
// then we don't need to generate it on the impl.
let trait_assoc_id = tcx.associated_type_for_effects(trait_id)?;
// FIXME(effects): span
let span = tcx.def_ident_span(def_id).unwrap();
let impl_assoc_ty = tcx.at(span).create_def(def_id, kw::Empty, DefKind::AssocTy);
let local_def_id = impl_assoc_ty.def_id();
let def_id = local_def_id.to_def_id();
impl_assoc_ty.def_ident_span(Some(span));
impl_assoc_ty.associated_item(ty::AssocItem {
name: kw::Empty,
kind: ty::AssocKind::Type,
def_id,
trait_item_def_id: Some(trait_assoc_id),
container: ty::ImplContainer,
fn_has_self_parameter: false,
opt_rpitit_info: None,
is_effects_desugaring: true,
});
// no default value.
impl_assoc_ty.defaultness(hir::Defaultness::Final);
// set the type of the associated type! If this is a const impl,
// we set to Maybe, otherwise we set to `Runtime`.
let type_def_id = if tcx.is_const_trait_impl_raw(impl_def_id.to_def_id()) {
tcx.require_lang_item(hir::LangItem::EffectsMaybe, Some(span))
} else {
tcx.require_lang_item(hir::LangItem::EffectsRuntime, Some(span))
};
// FIXME(effects): make impls use `Min` for their effect types
impl_assoc_ty.type_of(ty::EarlyBinder::bind(Ty::new_adt(
tcx,
tcx.adt_def(type_def_id),
ty::GenericArgs::empty(),
)));
(impl_assoc_ty, impl_def_id)
}
def_kind => bug!(
"associated_type_for_effects: {:?} should be Trait or Impl but is {:?}",
def_id,
def_kind
),
};
feed.feed_hir();
// visibility is public.
feed.visibility(ty::Visibility::Public);
// Copy generics_of of the trait/impl, making the trait/impl as parent.
feed.generics_of({
let parent_generics = tcx.generics_of(parent_did);
let parent_count = parent_generics.parent_count + parent_generics.own_params.len();
ty::Generics {
parent: Some(parent_did.to_def_id()),
parent_count,
own_params: vec![],
param_def_id_to_index: parent_generics.param_def_id_to_index.clone(),
has_self: false,
has_late_bound_regions: None,
host_effect_index: parent_generics.host_effect_index,
}
});
feed.explicit_item_super_predicates(ty::EarlyBinder::bind(&[]));
// There are no inferred outlives for the synthesized associated type.
feed.inferred_outlives_of(&[]);
Some(feed.def_id().to_def_id())
}
/// Given an `fn_def_id` of a trait or a trait implementation:
///
/// if `fn_def_id` is a function defined inside a trait, then it synthesizes
@ -494,7 +362,6 @@ fn associated_type_for_impl_trait_in_impl(
param_def_id_to_index,
has_self: false,
has_late_bound_regions: trait_assoc_generics.has_late_bound_regions,
host_effect_index: parent_generics.host_effect_index,
}
});

View File

@ -150,6 +150,16 @@ 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.
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();
let unnormalized_env =

View File

@ -108,7 +108,6 @@ impl<I: Interner> CanonicalVarInfo<I> {
CanonicalVarKind::PlaceholderRegion(..) => false,
CanonicalVarKind::Const(_) => true,
CanonicalVarKind::PlaceholderConst(_) => false,
CanonicalVarKind::Effect => true,
}
}
@ -118,17 +117,15 @@ impl<I: Interner> CanonicalVarInfo<I> {
CanonicalVarKind::Ty(_)
| CanonicalVarKind::PlaceholderTy(_)
| CanonicalVarKind::Const(_)
| CanonicalVarKind::PlaceholderConst(_)
| CanonicalVarKind::Effect => false,
| CanonicalVarKind::PlaceholderConst(_) => false,
}
}
pub fn expect_placeholder_index(self) -> usize {
match self.kind {
CanonicalVarKind::Ty(_)
| CanonicalVarKind::Region(_)
| CanonicalVarKind::Const(_)
| CanonicalVarKind::Effect => panic!("expected placeholder: {self:?}"),
CanonicalVarKind::Ty(_) | CanonicalVarKind::Region(_) | CanonicalVarKind::Const(_) => {
panic!("expected placeholder: {self:?}")
}
CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.var().as_usize(),
CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.var().as_usize(),
@ -161,9 +158,6 @@ pub enum CanonicalVarKind<I: Interner> {
/// Some kind of const inference variable.
Const(UniverseIndex),
/// Effect variable `'?E`.
Effect,
/// A "placeholder" that represents "any const".
PlaceholderConst(I::PlaceholderConst),
}
@ -180,7 +174,6 @@ impl<I: Interner> CanonicalVarKind<I> {
CanonicalVarKind::Ty(CanonicalTyVarKind::Float | CanonicalTyVarKind::Int) => {
UniverseIndex::ROOT
}
CanonicalVarKind::Effect => UniverseIndex::ROOT,
}
}
@ -205,8 +198,7 @@ impl<I: Interner> CanonicalVarKind<I> {
CanonicalVarKind::PlaceholderConst(placeholder) => {
CanonicalVarKind::PlaceholderConst(placeholder.with_updated_universe(ui))
}
CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float)
| CanonicalVarKind::Effect => {
CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => {
assert_eq!(ui, UniverseIndex::ROOT);
self
}
@ -311,10 +303,6 @@ impl<I: Interner> CanonicalVarValues<I> {
Region::new_anon_bound(cx, ty::INNERMOST, ty::BoundVar::from_usize(i))
.into()
}
CanonicalVarKind::Effect => {
Const::new_anon_bound(cx, ty::INNERMOST, ty::BoundVar::from_usize(i))
.into()
}
CanonicalVarKind::Const(_) | CanonicalVarKind::PlaceholderConst(_) => {
Const::new_anon_bound(cx, ty::INNERMOST, ty::BoundVar::from_usize(i))
.into()

View File

@ -84,32 +84,12 @@ rustc_index::newtype_index! {
pub struct ConstVid {}
}
rustc_index::newtype_index! {
/// An **effect** **v**ariable **ID**.
///
/// Handling effect infer variables happens separately from const infer variables
/// because we do not want to reuse any of the const infer machinery. If we try to
/// relate an effect variable with a normal one, we would ICE, which can catch bugs
/// where we are not correctly using the effect var for an effect param. Fallback
/// is also implemented on top of having separate effect and normal const variables.
#[encodable]
#[orderable]
#[debug_format = "?{}e"]
#[gate_rustc_only]
pub struct EffectVid {}
}
/// An inference variable for a const, for use in const generics.
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable))]
pub enum InferConst {
/// Infer the value of the const.
Var(ConstVid),
/// Infer the value of the effect.
///
/// For why this is separate from the `Var` variant above, see the
/// documentation on `EffectVid`.
EffectVar(EffectVid),
/// A fresh const variable. See `infer::freshen` for more details.
Fresh(u32),
}
@ -118,7 +98,6 @@ impl fmt::Debug for InferConst {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
InferConst::Var(var) => write!(f, "{var:?}"),
InferConst::EffectVar(var) => write!(f, "{var:?}"),
InferConst::Fresh(var) => write!(f, "Fresh({var:?})"),
}
}
@ -128,7 +107,7 @@ impl fmt::Debug for InferConst {
impl<CTX> HashStable<CTX> for InferConst {
fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
match self {
InferConst::Var(_) | InferConst::EffectVar(_) => {
InferConst::Var(_) => {
panic!("const variables should not be hashed: {self:?}")
}
InferConst::Fresh(i) => i.hash_stable(hcx, hasher),

View File

@ -1,59 +0,0 @@
use crate::Interner;
use crate::inherent::*;
use crate::lang_items::TraitSolverLangItem::{EffectsMaybe, EffectsNoRuntime, EffectsRuntime};
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum EffectKind {
Maybe,
Runtime,
NoRuntime,
}
impl EffectKind {
pub fn try_from_def_id<I: Interner>(cx: I, def_id: I::DefId) -> Option<EffectKind> {
if cx.is_lang_item(def_id, EffectsMaybe) {
Some(EffectKind::Maybe)
} else if cx.is_lang_item(def_id, EffectsRuntime) {
Some(EffectKind::Runtime)
} else if cx.is_lang_item(def_id, EffectsNoRuntime) {
Some(EffectKind::NoRuntime)
} else {
None
}
}
pub fn to_def_id<I: Interner>(self, cx: I) -> I::DefId {
let lang_item = match self {
EffectKind::Maybe => EffectsMaybe,
EffectKind::NoRuntime => EffectsNoRuntime,
EffectKind::Runtime => EffectsRuntime,
};
cx.require_lang_item(lang_item)
}
pub fn try_from_ty<I: Interner>(cx: I, ty: I::Ty) -> Option<EffectKind> {
if let crate::Adt(def, _) = ty.kind() {
Self::try_from_def_id(cx, def.def_id())
} else {
None
}
}
pub fn to_ty<I: Interner>(self, cx: I) -> I::Ty {
I::Ty::new_adt(cx, cx.adt_def(self.to_def_id(cx)), Default::default())
}
/// Returns an intersection between two effect kinds. If one effect kind
/// is more permissive than the other (e.g. `Maybe` vs `Runtime`), this
/// returns the less permissive effect kind (`Runtime`).
pub fn intersection(a: Self, b: Self) -> Option<Self> {
use EffectKind::*;
match (a, b) {
(Maybe, x) | (x, Maybe) => Some(x),
(Runtime, Runtime) => Some(Runtime),
(NoRuntime, NoRuntime) => Some(NoRuntime),
(Runtime, NoRuntime) | (NoRuntime, Runtime) => None,
}
}
}

View File

@ -4,7 +4,6 @@ use smallvec::smallvec;
use crate::data_structures::HashSet;
use crate::inherent::*;
use crate::lang_items::TraitSolverLangItem;
use crate::outlives::{Component, push_outlives_components};
use crate::{self as ty, Interner, Upcast as _};
@ -130,70 +129,6 @@ impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> {
return;
}
// HACK(effects): The following code is required to get implied bounds for effects associated
// types to work with super traits.
//
// Suppose `data` is a trait predicate with the form `<T as Tr>::Fx: EffectsCompat<somebool>`
// and we know that `trait Tr: ~const SuperTr`, we need to elaborate this predicate into
// `<T as SuperTr>::Fx: EffectsCompat<somebool>`.
//
// Since the semantics for elaborating bounds about effects is equivalent to elaborating
// bounds about super traits (elaborate `T: Tr` into `T: SuperTr`), we place effects elaboration
// next to super trait elaboration.
if cx.is_lang_item(data.def_id(), TraitSolverLangItem::EffectsCompat)
&& matches!(self.mode, Filter::All)
{
// first, ensure that the predicate we've got looks like a `<T as Tr>::Fx: EffectsCompat<somebool>`.
if let ty::Alias(ty::AliasTyKind::Projection, alias_ty) = data.self_ty().kind()
{
// look for effects-level bounds that look like `<Self as Tr>::Fx: TyCompat<<Self as SuperTr>::Fx>`
// on the trait, which is proof to us that `Tr: ~const SuperTr`. We're looking for bounds on the
// associated trait, so we use `explicit_implied_predicates_of` since it gives us more than just
// `Self: SuperTr` bounds.
let bounds = cx.explicit_implied_predicates_of(cx.parent(alias_ty.def_id));
// instantiate the implied bounds, so we get `<T as Tr>::Fx` and not `<Self as Tr>::Fx`.
let elaborated = bounds.iter_instantiated(cx, alias_ty.args).filter_map(
|(clause, _)| {
let ty::ClauseKind::Trait(tycompat_bound) =
clause.kind().skip_binder()
else {
return None;
};
if !cx.is_lang_item(
tycompat_bound.def_id(),
TraitSolverLangItem::EffectsTyCompat,
) {
return None;
}
// extract `<T as SuperTr>::Fx` from the `TyCompat` bound.
let supertrait_effects_ty =
tycompat_bound.trait_ref.args.type_at(1);
let ty::Alias(ty::AliasTyKind::Projection, supertrait_alias_ty) =
supertrait_effects_ty.kind()
else {
return None;
};
// The self types (`T`) must be equal for `<T as Tr>::Fx` and `<T as SuperTr>::Fx`.
if supertrait_alias_ty.self_ty() != alias_ty.self_ty() {
return None;
};
// replace the self type in the original bound `<T as Tr>::Fx: EffectsCompat<somebool>`
// to the effects type of the super trait. (`<T as SuperTr>::Fx`)
let elaborated_bound = data.with_self_ty(cx, supertrait_effects_ty);
Some(
elaboratable
.child(bound_clause.rebind(elaborated_bound).upcast(cx)),
)
},
);
self.extend_deduped(elaborated);
}
}
let map_to_child_clause =
|(index, (clause, span)): (usize, (I::Clause, I::Span))| {
elaboratable.child_with_derived_cause(
@ -220,6 +155,16 @@ impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> {
),
};
}
// `T: ~const Trait` implies `T: ~const Supertrait`.
ty::ClauseKind::HostEffect(data) => self.extend_deduped(
cx.implied_const_bounds(data.def_id()).iter_identity().map(|trait_ref| {
elaboratable.child(
trait_ref
.to_host_effect_clause(cx, data.host)
.instantiate_supertrait(cx, bound_clause.rebind(data.trait_ref)),
)
}),
),
ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty_max, r_min)) => {
// We know that `T: 'a` for some type `T`. We can
// often elaborate this. For example, if we know that

View File

@ -38,10 +38,6 @@ pub trait InferCtxtLike: Sized {
&self,
vid: ty::ConstVid,
) -> <Self::Interner as Interner>::Const;
fn opportunistic_resolve_effect_var(
&self,
vid: ty::EffectVid,
) -> <Self::Interner as Interner>::Const;
fn opportunistic_resolve_lt_var(
&self,
vid: ty::RegionVid,
@ -71,7 +67,6 @@ pub trait InferCtxtLike: Sized {
fn equate_int_vids_raw(&self, a: ty::IntVid, b: ty::IntVid);
fn equate_float_vids_raw(&self, a: ty::FloatVid, b: ty::FloatVid);
fn equate_const_vids_raw(&self, a: ty::ConstVid, b: ty::ConstVid);
fn equate_effect_vids_raw(&self, a: ty::EffectVid, b: ty::EffectVid);
fn instantiate_ty_var_raw<R: PredicateEmittingRelation<Self>>(
&self,
@ -83,11 +78,6 @@ pub trait InferCtxtLike: Sized {
) -> RelateResult<Self::Interner, ()>;
fn instantiate_int_var_raw(&self, vid: ty::IntVid, value: ty::IntVarValue);
fn instantiate_float_var_raw(&self, vid: ty::FloatVid, value: ty::FloatVarValue);
fn instantiate_effect_var_raw(
&self,
vid: ty::EffectVid,
value: <Self::Interner as Interner>::Const,
);
fn instantiate_const_var_raw<R: PredicateEmittingRelation<Self>>(
&self,
relation: &mut R,

View File

@ -468,6 +468,14 @@ pub trait Clause<I: Interner<Clause = Self>>:
.transpose()
}
fn as_host_effect_clause(self) -> Option<ty::Binder<I, ty::HostEffectPredicate<I>>> {
self.kind()
.map_bound(
|clause| if let ty::ClauseKind::HostEffect(t) = clause { Some(t) } else { None },
)
.transpose()
}
fn as_projection_clause(self) -> Option<ty::Binder<I, ty::ProjectionPredicate<I>>> {
self.kind()
.map_bound(

View File

@ -24,6 +24,7 @@ pub trait Interner:
+ IrPrint<ty::AliasTerm<Self>>
+ IrPrint<ty::TraitRef<Self>>
+ IrPrint<ty::TraitPredicate<Self>>
+ IrPrint<ty::HostEffectPredicate<Self>>
+ IrPrint<ty::ExistentialTraitRef<Self>>
+ IrPrint<ty::ExistentialProjection<Self>>
+ IrPrint<ty::ProjectionPredicate<Self>>
@ -228,6 +229,16 @@ pub trait Interner:
def_id: Self::DefId,
) -> ty::EarlyBinder<Self, impl IntoIterator<Item = (Self::Clause, Self::Span)>>;
fn is_const_impl(self, def_id: Self::DefId) -> bool;
fn const_conditions(
self,
def_id: Self::DefId,
) -> ty::EarlyBinder<Self, impl IntoIterator<Item = ty::Binder<Self, ty::TraitRef<Self>>>>;
fn implied_const_bounds(
self,
def_id: Self::DefId,
) -> ty::EarlyBinder<Self, impl IntoIterator<Item = ty::Binder<Self, ty::TraitRef<Self>>>>;
fn has_target_features(self, def_id: Self::DefId) -> bool;
fn require_lang_item(self, lang_item: TraitSolverLangItem) -> Self::DefId;

View File

@ -2,8 +2,8 @@ use std::fmt;
use crate::{
AliasTerm, AliasTy, Binder, CoercePredicate, ExistentialProjection, ExistentialTraitRef, FnSig,
Interner, NormalizesTo, OutlivesPredicate, ProjectionPredicate, SubtypePredicate,
TraitPredicate, TraitRef,
HostEffectPredicate, Interner, NormalizesTo, OutlivesPredicate, ProjectionPredicate,
SubtypePredicate, TraitPredicate, TraitRef,
};
pub trait IrPrint<T> {
@ -53,6 +53,7 @@ define_display_via_print!(
NormalizesTo,
SubtypePredicate,
CoercePredicate,
HostEffectPredicate,
AliasTy,
AliasTerm,
FnSig,

View File

@ -20,13 +20,6 @@ pub enum TraitSolverLangItem {
Destruct,
DiscriminantKind,
DynMetadata,
EffectsCompat,
EffectsIntersection,
EffectsIntersectionOutput,
EffectsMaybe,
EffectsNoRuntime,
EffectsRuntime,
EffectsTyCompat,
Fn,
FnMut,
FnOnce,

View File

@ -43,7 +43,6 @@ mod macros;
mod binder;
mod canonical;
mod const_kind;
mod effects;
mod flags;
mod generic_arg;
mod infer_ctxt;
@ -67,7 +66,6 @@ pub use canonical::*;
#[cfg(feature = "nightly")]
pub use codec::*;
pub use const_kind::*;
pub use effects::*;
pub use flags::*;
pub use generic_arg::*;
pub use infer_ctxt::*;

View File

@ -111,6 +111,13 @@ impl<I: Interner> ty::Binder<I, TraitRef<I>> {
pub fn def_id(&self) -> I::DefId {
self.skip_binder().def_id
}
pub fn to_host_effect_clause(self, cx: I, host: HostPolarity) -> I::Clause {
self.map_bound(|trait_ref| {
ty::ClauseKind::HostEffect(HostEffectPredicate { trait_ref, host })
})
.upcast(cx)
}
}
#[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)]
@ -745,6 +752,64 @@ impl<I: Interner> fmt::Debug for NormalizesTo<I> {
}
}
#[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)]
#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)]
#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))]
pub struct HostEffectPredicate<I: Interner> {
pub trait_ref: ty::TraitRef<I>,
pub host: HostPolarity,
}
impl<I: Interner> HostEffectPredicate<I> {
pub fn self_ty(self) -> I::Ty {
self.trait_ref.self_ty()
}
pub fn with_self_ty(self, interner: I, self_ty: I::Ty) -> Self {
Self { trait_ref: self.trait_ref.with_self_ty(interner, self_ty), ..self }
}
pub fn def_id(self) -> I::DefId {
self.trait_ref.def_id
}
}
impl<I: Interner> ty::Binder<I, HostEffectPredicate<I>> {
pub fn def_id(self) -> I::DefId {
// Ok to skip binder since trait `DefId` does not care about regions.
self.skip_binder().def_id()
}
pub fn self_ty(self) -> ty::Binder<I, I::Ty> {
self.map_bound(|trait_ref| trait_ref.self_ty())
}
#[inline]
pub fn host(self) -> HostPolarity {
self.skip_binder().host
}
}
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))]
pub enum HostPolarity {
/// May be called in const environments if the callee is const.
Maybe,
/// Always allowed to be called in const environments.
Const,
}
impl HostPolarity {
pub fn satisfies(self, goal: HostPolarity) -> bool {
match (self, goal) {
(HostPolarity::Const, HostPolarity::Const | HostPolarity::Maybe) => true,
(HostPolarity::Maybe, HostPolarity::Maybe) => true,
(HostPolarity::Maybe, HostPolarity::Const) => false,
}
}
}
/// Encodes that `a` must be a subtype of `b`. The `a_is_expected` flag indicates
/// whether the `a` type is the type that we should label as "expected" when
/// presenting user diagnostics.

View File

@ -37,6 +37,12 @@ pub enum ClauseKind<I: Interner> {
/// Constant initializer must evaluate successfully.
ConstEvaluatable(I::Const),
/// Enforces the constness of the predicate we're calling. Like a projection
/// goal from a where clause, it's always going to be paired with a
/// corresponding trait clause; this just enforces the *constness* of that
/// implementation.
HostEffect(ty::HostEffectPredicate<I>),
}
#[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)]
@ -110,6 +116,7 @@ impl<I: Interner> fmt::Debug for ClauseKind<I> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ClauseKind::ConstArgHasType(ct, ty) => write!(f, "ConstArgHasType({ct:?}, {ty:?})"),
ClauseKind::HostEffect(data) => data.fmt(f),
ClauseKind::Trait(a) => a.fmt(f),
ClauseKind::RegionOutlives(pair) => pair.fmt(f),
ClauseKind::TypeOutlives(pair) => pair.fmt(f),

View File

@ -179,23 +179,9 @@ where
Ok(a)
}
(
ty::ConstKind::Infer(ty::InferConst::EffectVar(a_vid)),
ty::ConstKind::Infer(ty::InferConst::EffectVar(b_vid)),
) => {
infcx.equate_effect_vids_raw(a_vid, b_vid);
Ok(a)
}
// All other cases of inference with other variables are errors.
(
ty::ConstKind::Infer(ty::InferConst::Var(_) | ty::InferConst::EffectVar(_)),
ty::ConstKind::Infer(_),
)
| (
ty::ConstKind::Infer(_),
ty::ConstKind::Infer(ty::InferConst::Var(_) | ty::InferConst::EffectVar(_)),
) => {
(ty::ConstKind::Infer(ty::InferConst::Var(_)), ty::ConstKind::Infer(_))
| (ty::ConstKind::Infer(_), ty::ConstKind::Infer(ty::InferConst::Var(_))) => {
panic!(
"tried to combine ConstKind::Infer/ConstKind::Infer(InferConst::Var): {a:?} and {b:?}"
)
@ -211,16 +197,6 @@ where
Ok(a)
}
(ty::ConstKind::Infer(ty::InferConst::EffectVar(vid)), _) => {
infcx.instantiate_effect_var_raw(vid, b);
Ok(b)
}
(_, ty::ConstKind::Infer(ty::InferConst::EffectVar(vid))) => {
infcx.instantiate_effect_var_raw(vid, a);
Ok(a)
}
(ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..))
if infcx.cx().features().generic_const_exprs() || infcx.next_trait_solver() =>
{

View File

@ -1393,7 +1393,6 @@ pub struct Generics {
pub param_def_id_to_index: Vec<(GenericDef, u32)>,
pub has_self: bool,
pub has_late_bound_regions: Option<Span>,
pub host_effect_index: Option<usize>,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]

View File

@ -1069,47 +1069,3 @@ pub trait FnPtr: Copy + Clone {
pub macro SmartPointer($item:item) {
/* compiler built-in */
}
// Support traits and types for the desugaring of const traits and
// `~const` bounds. Not supposed to be used by anything other than
// the compiler.
#[doc(hidden)]
#[unstable(
feature = "effect_types",
issue = "none",
reason = "internal module for implementing effects"
)]
#[allow(missing_debug_implementations)] // these unit structs don't need `Debug` impls.
pub mod effects {
#[lang = "EffectsNoRuntime"]
pub struct NoRuntime;
#[lang = "EffectsMaybe"]
pub struct Maybe;
#[lang = "EffectsRuntime"]
pub struct Runtime;
#[lang = "EffectsCompat"]
pub trait Compat<#[rustc_runtime] const RUNTIME: bool> {}
impl Compat<false> for NoRuntime {}
impl Compat<true> for Runtime {}
impl<#[rustc_runtime] const RUNTIME: bool> Compat<RUNTIME> for Maybe {}
#[lang = "EffectsTyCompat"]
#[marker]
pub trait TyCompat<T: ?Sized> {}
impl<T: ?Sized> TyCompat<T> for T {}
impl<T: ?Sized> TyCompat<Maybe> for T {}
#[lang = "EffectsIntersection"]
pub trait Intersection {
#[lang = "EffectsIntersectionOutput"]
type Output: ?Sized;
}
// FIXME(effects): remove this after next trait solver lands
impl Intersection for () {
type Output = Maybe;
}
}

View File

@ -156,7 +156,7 @@ fn clean_param_env<'tcx>(
.iter()
.inspect(|param| {
if cfg!(debug_assertions) {
debug_assert!(!param.is_anonymous_lifetime() && !param.is_host_effect());
debug_assert!(!param.is_anonymous_lifetime());
if let ty::GenericParamDefKind::Type { synthetic, .. } = param.kind {
debug_assert!(!synthetic && param.name != kw::SelfUpper);
}

View File

@ -368,7 +368,9 @@ pub(crate) fn clean_predicate<'tcx>(
// FIXME(generic_const_exprs): should this do something?
ty::ClauseKind::ConstEvaluatable(..)
| ty::ClauseKind::WellFormed(..)
| ty::ClauseKind::ConstArgHasType(..) => None,
| ty::ClauseKind::ConstArgHasType(..)
// FIXME(effects): We can probably use this `HostEffect` pred to render `~const`.
| ty::ClauseKind::HostEffect(_) => None,
}
}
@ -542,7 +544,7 @@ fn clean_generic_param_def(
synthetic,
})
}
ty::GenericParamDefKind::Const { has_default, synthetic, is_host_effect: _ } => {
ty::GenericParamDefKind::Const { has_default, synthetic } => {
(def.name, GenericParamDefKind::Const {
ty: Box::new(clean_middle_ty(
ty::Binder::dummy(
@ -617,7 +619,7 @@ fn clean_generic_param<'tcx>(
synthetic,
})
}
hir::GenericParamKind::Const { ty, default, synthetic, is_host_effect: _ } => {
hir::GenericParamKind::Const { ty, default, synthetic } => {
(param.name.ident().name, GenericParamDefKind::Const {
ty: Box::new(clean_ty(ty, cx)),
default: default.map(|ct| {
@ -797,7 +799,7 @@ fn clean_ty_generics<'tcx>(
}
true
}
ty::GenericParamDefKind::Const { is_host_effect, .. } => !is_host_effect,
ty::GenericParamDefKind::Const { .. } => true,
})
.map(|param| clean_generic_param_def(param, ParamDefaults::Yes, cx))
.collect();

View File

@ -1366,8 +1366,7 @@ impl GenericParamDef {
pub(crate) fn is_synthetic_param(&self) -> bool {
match self.kind {
GenericParamDefKind::Lifetime { .. } => false,
GenericParamDefKind::Const { synthetic: is_host_effect, .. } => is_host_effect,
GenericParamDefKind::Lifetime { .. } | GenericParamDefKind::Const { .. } => false,
GenericParamDefKind::Type { synthetic, .. } => synthetic,
}
}

Some files were not shown because too many files have changed in this diff Show More