Skip to content
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

Various fixes for const_trait_impl #89359

Merged
merged 4 commits into from
Nov 25, 2021
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
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>> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So ~const Drop bounds already are unlike Drop bounds in that the type does not have to actually implement the Drop trait. Is this noted anywhere?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. That makes sense now, seeing as there isn't really any other way to easily do it.

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() {}