Skip to content

Allow trait A: ~const B #93429

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1272,7 +1272,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.visit_vis(&item.vis);
self.visit_ident(item.ident);
self.visit_generics(generics);
self.with_banned_tilde_const(|this| {
self.with_tilde_const_allowed(|this| {
walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits)
});
walk_list!(self, visit_assoc_item, items, AssocCtxt::Trait);
Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_infer/src/traits/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,12 @@ impl<'tcx> Elaborator<'tcx> {
// Get predicates declared on the trait.
let predicates = tcx.super_predicates_of(data.def_id());

let obligations = predicates.predicates.iter().map(|&(pred, _)| {
let obligations = predicates.predicates.iter().map(|&(mut pred, _)| {
// when parent predicate is non-const, elaborate it to non-const predicates.
if data.constness == ty::BoundConstness::NotConst {
pred = pred.without_const(tcx);
}

predicate_obligation(
pred.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)),
obligation.param_env,
Expand Down
43 changes: 35 additions & 8 deletions compiler/rustc_trait_selection/src/traits/wf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ pub fn trait_obligations<'a, 'tcx>(
infcx: &InferCtxt<'a, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
body_id: hir::HirId,
trait_ref: &ty::TraitRef<'tcx>,
trait_pred: &ty::TraitPredicate<'tcx>,
span: Span,
item: &'tcx hir::Item<'tcx>,
) -> Vec<traits::PredicateObligation<'tcx>> {
Expand All @@ -98,7 +98,7 @@ pub fn trait_obligations<'a, 'tcx>(
recursion_depth: 0,
item: Some(item),
};
wf.compute_trait_ref(trait_ref, Elaborate::All);
wf.compute_trait_pred(trait_pred, Elaborate::All);
debug!(obligations = ?wf.out);
wf.normalize(infcx)
}
Expand All @@ -123,7 +123,7 @@ pub fn predicate_obligations<'a, 'tcx>(
// It's ok to skip the binder here because wf code is prepared for it
match predicate.kind().skip_binder() {
ty::PredicateKind::Trait(t) => {
wf.compute_trait_ref(&t.trait_ref, Elaborate::None);
wf.compute_trait_pred(&t, Elaborate::None);
}
ty::PredicateKind::RegionOutlives(..) => {}
ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) => {
Expand Down Expand Up @@ -301,11 +301,18 @@ impl<'tcx> WfPredicates<'tcx> {
}

/// Pushes the obligations required for `trait_ref` to be WF into `self.out`.
fn compute_trait_ref(&mut self, trait_ref: &ty::TraitRef<'tcx>, elaborate: Elaborate) {
fn compute_trait_pred(&mut self, trait_pred: &ty::TraitPredicate<'tcx>, elaborate: Elaborate) {
let tcx = self.tcx;
let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.substs);
let trait_ref = &trait_pred.trait_ref;

debug!("compute_trait_ref obligations {:?}", obligations);
// if the trait predicate is not const, the wf obligations should not be const as well.
let obligations = if trait_pred.constness == ty::BoundConstness::NotConst {
self.nominal_obligations_without_const(trait_ref.def_id, trait_ref.substs)
} else {
self.nominal_obligations(trait_ref.def_id, trait_ref.substs)
};

debug!("compute_trait_pred obligations {:?}", obligations);
let param_env = self.param_env;
let depth = self.recursion_depth;

Expand Down Expand Up @@ -685,10 +692,11 @@ impl<'tcx> WfPredicates<'tcx> {
}

#[instrument(level = "debug", skip(self))]
fn nominal_obligations(
fn nominal_obligations_inner(
&mut self,
def_id: DefId,
substs: SubstsRef<'tcx>,
remap_constness: bool,
) -> Vec<traits::PredicateObligation<'tcx>> {
let predicates = self.tcx.predicates_of(def_id);
let mut origins = vec![def_id; predicates.predicates.len()];
Expand All @@ -703,19 +711,38 @@ impl<'tcx> WfPredicates<'tcx> {
debug_assert_eq!(predicates.predicates.len(), origins.len());

iter::zip(iter::zip(predicates.predicates, predicates.spans), origins.into_iter().rev())
.map(|((pred, span), origin_def_id)| {
.map(|((mut pred, span), origin_def_id)| {
let code = if span.is_dummy() {
traits::MiscObligation
} else {
traits::BindingObligation(origin_def_id, span)
};
let cause = self.cause(code);
if remap_constness {
pred = pred.without_const(self.tcx);
}
traits::Obligation::with_depth(cause, self.recursion_depth, self.param_env, pred)
})
.filter(|pred| !pred.has_escaping_bound_vars())
.collect()
}

fn nominal_obligations(
&mut self,
def_id: DefId,
substs: SubstsRef<'tcx>,
) -> Vec<traits::PredicateObligation<'tcx>> {
self.nominal_obligations_inner(def_id, substs, false)
}

fn nominal_obligations_without_const(
&mut self,
def_id: DefId,
substs: SubstsRef<'tcx>,
) -> Vec<traits::PredicateObligation<'tcx>> {
self.nominal_obligations_inner(def_id, substs, true)
}

fn from_object_ty(
&mut self,
ty: Ty<'tcx>,
Expand Down
13 changes: 11 additions & 2 deletions compiler/rustc_typeck/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) {
// We match on both `ty::ImplPolarity` and `ast::ImplPolarity` just to get the `!` span.
match (tcx.impl_polarity(def_id), impl_.polarity) {
(ty::ImplPolarity::Positive, _) => {
check_impl(tcx, item, impl_.self_ty, &impl_.of_trait);
check_impl(tcx, item, impl_.self_ty, &impl_.of_trait, impl_.constness);
}
(ty::ImplPolarity::Negative, ast::ImplPolarity::Negative(span)) => {
// FIXME(#27579): what amount of WF checking do we need for neg impls?
Expand Down Expand Up @@ -1242,6 +1242,7 @@ fn check_impl<'tcx>(
item: &'tcx hir::Item<'tcx>,
ast_self_ty: &hir::Ty<'_>,
ast_trait_ref: &Option<hir::TraitRef<'_>>,
constness: hir::Constness,
) {
enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| {
match *ast_trait_ref {
Expand All @@ -1251,11 +1252,19 @@ fn check_impl<'tcx>(
// won't hold).
let trait_ref = tcx.impl_trait_ref(item.def_id).unwrap();
let trait_ref = wfcx.normalize(ast_trait_ref.path.span, None, trait_ref);
let trait_pred = ty::TraitPredicate {
trait_ref,
constness: match constness {
hir::Constness::Const => ty::BoundConstness::ConstIfConst,
hir::Constness::NotConst => ty::BoundConstness::NotConst,
},
polarity: ty::ImplPolarity::Positive,
};
let obligations = traits::wf::trait_obligations(
wfcx.infcx,
wfcx.param_env,
wfcx.body_id,
&trait_ref,
&trait_pred,
ast_trait_ref.path.span,
item,
);
Expand Down
14 changes: 14 additions & 0 deletions src/test/ui/rfc-2632-const-trait-impl/super-traits-fail-2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#![feature(const_trait_impl)]

trait Foo {
fn a(&self);
}
trait Bar: ~const Foo {}

const fn foo<T: Bar>(x: &T) {
x.a();
//~^ ERROR the trait bound
//~| ERROR cannot call
}

fn main() {}
24 changes: 24 additions & 0 deletions src/test/ui/rfc-2632-const-trait-impl/super-traits-fail-2.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
error[E0277]: the trait bound `T: ~const Foo` is not satisfied
--> $DIR/super-traits-fail-2.rs:9:7
|
LL | x.a();
| ^^^ the trait `~const Foo` is not implemented for `T`
|
note: the trait `Foo` is implemented for `T`, but that implementation is not `const`
--> $DIR/super-traits-fail-2.rs:9:7
|
LL | x.a();
| ^^^

error[E0015]: cannot call non-const fn `<T as Foo>::a` in constant functions
--> $DIR/super-traits-fail-2.rs:9:7
|
LL | x.a();
| ^^^
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0015, E0277.
For more information about an error, try `rustc --explain E0015`.
16 changes: 16 additions & 0 deletions src/test/ui/rfc-2632-const-trait-impl/super-traits-fail.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#![feature(const_trait_impl)]

trait Foo {
fn a(&self);
}
trait Bar: ~const Foo {}

struct S;
impl Foo for S {
fn a(&self) {}
}

impl const Bar for S {}
//~^ ERROR the trait bound

fn main() {}
24 changes: 24 additions & 0 deletions src/test/ui/rfc-2632-const-trait-impl/super-traits-fail.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
error[E0277]: the trait bound `S: ~const Foo` is not satisfied
--> $DIR/super-traits-fail.rs:13:12
|
LL | impl const Bar for S {}
| ^^^ the trait `~const Foo` is not implemented for `S`
|
note: the trait `Foo` is implemented for `S`, but that implementation is not `const`
--> $DIR/super-traits-fail.rs:13:12
|
LL | impl const Bar for S {}
| ^^^
note: required by a bound in `Bar`
--> $DIR/super-traits-fail.rs:6:12
|
LL | trait Bar: ~const Foo {}
| ^^^^^^^^^^ required by this bound in `Bar`
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
LL | impl const Bar for S where S: ~const Foo {}
| +++++++++++++++++++

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.
22 changes: 22 additions & 0 deletions src/test/ui/rfc-2632-const-trait-impl/super-traits.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// check-pass
#![feature(const_trait_impl)]

trait Foo {
fn a(&self);
}
trait Bar: ~const Foo {}

struct S;
impl const Foo for S {
fn a(&self) {}
}

impl const Bar for S {}

const fn foo<T: ~const Bar>(t: &T) {
t.a();
}

const _: () = foo(&S);

fn main() {}