Skip to content

Commit

Permalink
Rollup merge of rust-lang#89359 - fee1-dead:const-it, r=oli-obk
Browse files Browse the repository at this point in the history
Various fixes for const_trait_impl

A few problems I found while making `Iterator` easier to const-implement.

1. More generous `~const Drop` check.

We check for nested fields with caller bounds.

For example, an ADT type with fields of types `A`, `B`, `C`, check if all of them are either:
 - Bounded (`A: ~const Drop`, `B: Copy`)
 - Known to be able to destruct at compile time (`C = i32`, `struct C(i32)`, `C = some_fn`)

2. Don't treat trait functions marked with `#[default_method_body_is_const]` as stable const fns when checking `const_for` and `const_try` feature gates.

I think anyone can review this, so no r? this time.
  • Loading branch information
matthiaskrgr committed Nov 25, 2021
2 parents 3b149fc + 4f29f3c commit 57e784e
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 11 deletions.
9 changes: 5 additions & 4 deletions compiler/rustc_const_eval/src/transform/check_consts/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1004,11 +1004,12 @@ impl Visitor<'tcx> for Checker<'mir, 'tcx> {
}

let mut err_span = self.span;
let ty_of_dropped_place = dropped_place.ty(self.body, self.tcx).ty;

let ty_needs_non_const_drop = qualifs::NeedsNonConstDrop::in_any_value_of_ty(
self.ccx,
dropped_place.ty(self.body, self.tcx).ty,
);
let ty_needs_non_const_drop =
qualifs::NeedsNonConstDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place);

debug!(?ty_of_dropped_place, ?ty_needs_non_const_drop);

if !ty_needs_non_const_drop {
return;
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_passes/src/check_const.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,12 @@ impl<'tcx> CheckConstVisitor<'tcx> {
None => return true,
};

// If the function belongs to a trait, then it must enable the const_trait_impl
// feature to use that trait function (with a const default body).
if tcx.trait_of_item(def_id).is_some() {
return true;
}

// If this crate is not using stability attributes, or this function is not claiming to be a
// stable `const fn`, that is all that is required.
if !tcx.features().staged_api || tcx.has_attr(def_id, sym::rustc_const_unstable) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
&& obligation.predicate.skip_binder().constness == ty::BoundConstness::ConstIfConst
{
if self.is_in_const_context {
self.assemble_const_drop_candidates(obligation, &mut candidates)?;
self.assemble_const_drop_candidates(obligation, stack, &mut candidates)?;
} else {
debug!("passing ~const Drop bound; in non-const context");
// `~const Drop` when we are not in a const context has no effect.
Expand Down Expand Up @@ -911,9 +911,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}

fn assemble_const_drop_candidates(
fn assemble_const_drop_candidates<'a>(
&mut self,
obligation: &TraitObligation<'tcx>,
obligation_stack: &TraitObligationStack<'a, 'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) -> Result<(), SelectionError<'tcx>> {
let mut stack: Vec<(Ty<'tcx>, usize)> = vec![(obligation.self_ty().skip_binder(), 0)];
Expand All @@ -922,7 +923,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let mut noreturn = false;

self.check_recursion_depth(depth, obligation)?;
let mut copy_candidates = SelectionCandidateSet { vec: Vec::new(), ambiguous: false };
let mut new_candidates = SelectionCandidateSet { vec: Vec::new(), ambiguous: false };
let mut copy_obligation =
obligation.with(obligation.predicate.rebind(ty::TraitPredicate {
trait_ref: ty::TraitRef {
Expand All @@ -933,13 +934,29 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
polarity: ty::ImplPolarity::Positive,
}));
copy_obligation.recursion_depth = depth + 1;
self.assemble_candidates_from_impls(&copy_obligation, &mut copy_candidates);
self.assemble_candidates_from_impls(&copy_obligation, &mut new_candidates);
let copy_conditions = self.copy_clone_conditions(&copy_obligation);
self.assemble_builtin_bound_candidates(copy_conditions, &mut copy_candidates);
if !copy_candidates.vec.is_empty() {
self.assemble_builtin_bound_candidates(copy_conditions, &mut new_candidates);
let copy_stack = self.push_stack(obligation_stack.list(), &copy_obligation);
self.assemble_candidates_from_caller_bounds(&copy_stack, &mut new_candidates)?;

let const_drop_obligation =
obligation.with(obligation.predicate.rebind(ty::TraitPredicate {
trait_ref: ty::TraitRef {
def_id: self.tcx().require_lang_item(hir::LangItem::Drop, None),
substs: self.tcx().mk_substs_trait(ty, &[]),
},
constness: ty::BoundConstness::ConstIfConst,
polarity: ty::ImplPolarity::Positive,
}));

let const_drop_stack = self.push_stack(obligation_stack.list(), &const_drop_obligation);
self.assemble_candidates_from_caller_bounds(&const_drop_stack, &mut new_candidates)?;

if !new_candidates.vec.is_empty() {
noreturn = true;
}
debug!(?copy_candidates.vec, "assemble_const_drop_candidates - copy");
debug!(?new_candidates.vec, "assemble_const_drop_candidates");

match ty.kind() {
ty::Int(_)
Expand Down
20 changes: 20 additions & 0 deletions src/test/ui/rfc-2632-const-trait-impl/const-drop-bound.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// check-pass

#![feature(const_trait_impl)]
#![feature(const_fn_trait_bound)]
#![feature(const_precise_live_drops)]

const fn foo<T, E>(res: Result<T, E>) -> Option<T> where E: ~const Drop {
match res {
Ok(t) => Some(t),
Err(_e) => None,
}
}

pub struct Foo<T>(T);

const fn baz<T: ~const Drop, E: ~const Drop>(res: Result<Foo<T>, Foo<E>>) -> Option<Foo<T>> {
foo(res)
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// check-pass

#![feature(staged_api)]
#![feature(const_trait_impl)]
#![feature(const_fn_trait_bound)]
#![feature(const_t_try)]
#![feature(const_try)]
#![feature(try_trait_v2)]

#![stable(feature = "foo", since = "1.0")]

use std::ops::{ControlFlow, FromResidual, Try};

#[stable(feature = "foo", since = "1.0")]
pub struct T;

#[stable(feature = "foo", since = "1.0")]
#[rustc_const_unstable(feature = "const_t_try", issue = "none")]
impl const Try for T {
type Output = T;
type Residual = T;

fn from_output(t: T) -> T {
t
}

fn branch(self) -> ControlFlow<T, T> {
ControlFlow::Continue(self)
}
}

#[stable(feature = "foo", since = "1.0")]
#[rustc_const_unstable(feature = "const_t_try", issue = "none")]
impl const FromResidual for T {
fn from_residual(t: T) -> T {
t
}
}

#[stable(feature = "foo", since = "1.0")]
pub trait Tr {
#[default_method_body_is_const]
#[stable(feature = "foo", since = "1.0")]
fn bar() -> T {
T?
// Should be allowed.
// Must enable unstable features to call this trait fn in const contexts.
}
}

fn main() {}

0 comments on commit 57e784e

Please sign in to comment.