Skip to content

Commit

Permalink
Auto merge of #93429 - fee1-dead-contrib:allow-super-trait-tilde-cons…
Browse files Browse the repository at this point in the history
…t, r=oli-obk

Allow `trait A: ~const B`

What's included: a minimal working change set for `~const` supertraits to work.

r? `@oli-obk`
  • Loading branch information
bors committed Jul 24, 2022
2 parents 35a0617 + 6660227 commit b4151a4
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 12 deletions.
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() {}

0 comments on commit b4151a4

Please sign in to comment.