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

Allow using generic trait methods in const fn #79287

Merged
merged 4 commits into from
Nov 23, 2020
Merged
Show file tree
Hide file tree
Changes from 3 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_infer/src/traits/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ impl<'tcx, I: Iterator<Item = PredicateObligation<'tcx>>> Iterator for FilterToT
fn next(&mut self) -> Option<ty::PolyTraitRef<'tcx>> {
while let Some(obligation) = self.base_iterator.next() {
if let Some(data) = obligation.predicate.to_opt_poly_trait_ref() {
return Some(data);
return Some(data.value);
}
}
None
Expand Down
9 changes: 5 additions & 4 deletions compiler/rustc_middle/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use crate::ty::{self, AdtKind, Ty, TyCtxt};
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::Constness;
use rustc_span::symbol::Symbol;
use rustc_span::{Span, DUMMY_SP};
use smallvec::SmallVec;
Expand Down Expand Up @@ -457,7 +458,7 @@ pub enum ImplSource<'tcx, N> {
/// for some type parameter. The `Vec<N>` represents the
/// obligations incurred from normalizing the where-clause (if
/// any).
Param(Vec<N>),
Param(Vec<N>, Constness),

/// Virtual calls through an object.
Object(ImplSourceObjectData<'tcx, N>),
Expand Down Expand Up @@ -487,7 +488,7 @@ impl<'tcx, N> ImplSource<'tcx, N> {
pub fn nested_obligations(self) -> Vec<N> {
match self {
ImplSource::UserDefined(i) => i.nested,
ImplSource::Param(n) => n,
ImplSource::Param(n, _) => n,
ImplSource::Builtin(i) => i.nested,
ImplSource::AutoImpl(d) => d.nested,
ImplSource::Closure(c) => c.nested,
Expand All @@ -502,7 +503,7 @@ impl<'tcx, N> ImplSource<'tcx, N> {
pub fn borrow_nested_obligations(&self) -> &[N] {
match &self {
ImplSource::UserDefined(i) => &i.nested[..],
ImplSource::Param(n) => &n[..],
ImplSource::Param(n, _) => &n[..],
ImplSource::Builtin(i) => &i.nested[..],
ImplSource::AutoImpl(d) => &d.nested[..],
ImplSource::Closure(c) => &c.nested[..],
Expand All @@ -524,7 +525,7 @@ impl<'tcx, N> ImplSource<'tcx, N> {
substs: i.substs,
nested: i.nested.into_iter().map(f).collect(),
}),
ImplSource::Param(n) => ImplSource::Param(n.into_iter().map(f).collect()),
ImplSource::Param(n, ct) => ImplSource::Param(n.into_iter().map(f).collect(), ct),
ImplSource::Builtin(i) => ImplSource::Builtin(ImplSourceBuiltinData {
nested: i.nested.into_iter().map(f).collect(),
}),
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ pub enum SelectionCandidate<'tcx> {
/// `false` if there are no *further* obligations.
has_nested: bool,
},
ParamCandidate(ty::PolyTraitRef<'tcx>),
ParamCandidate(ty::ConstnessAnd<ty::PolyTraitRef<'tcx>>),
ImplCandidate(DefId),
AutoImplCandidate(DefId),

Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_middle/src/traits/structural_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> {

super::ImplSource::Object(ref d) => write!(f, "{:?}", d),

super::ImplSource::Param(ref n) => write!(f, "ImplSourceParamData({:?})", n),
super::ImplSource::Param(ref n, ct) => {
write!(f, "ImplSourceParamData({:?}, {:?})", n, ct)
}

super::ImplSource::Builtin(ref d) => write!(f, "{:?}", d),

Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE};
use rustc_hir::definitions::{DefPathHash, Definitions};
use rustc_hir::intravisit::Visitor;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{HirId, ItemKind, ItemLocalId, ItemLocalMap, ItemLocalSet, Node, TraitCandidate};
use rustc_hir::{
Constness, HirId, ItemKind, ItemLocalId, ItemLocalMap, ItemLocalSet, Node, TraitCandidate,
};
use rustc_index::vec::{Idx, IndexVec};
use rustc_macros::HashStable;
use rustc_session::config::{BorrowckMode, CrateType, OutputFilenames};
Expand Down Expand Up @@ -1635,6 +1637,8 @@ nop_list_lift! {projs; ProjectionKind => ProjectionKind}
// This is the impl for `&'a InternalSubsts<'a>`.
nop_list_lift! {substs; GenericArg<'a> => GenericArg<'tcx>}

CloneLiftImpls! { for<'tcx> { Constness, } }

pub mod tls {
use super::{ptr_eq, GlobalCtxt, TyCtxt};

Expand Down
8 changes: 5 additions & 3 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1503,9 +1503,11 @@ impl<'tcx> ToPredicate<'tcx> for PolyProjectionPredicate<'tcx> {
}

impl<'tcx> Predicate<'tcx> {
pub fn to_opt_poly_trait_ref(self) -> Option<PolyTraitRef<'tcx>> {
pub fn to_opt_poly_trait_ref(self) -> Option<ConstnessAnd<PolyTraitRef<'tcx>>> {
match self.skip_binders() {
PredicateAtom::Trait(t, _) => Some(ty::Binder::bind(t.trait_ref)),
PredicateAtom::Trait(t, constness) => {
Some(ConstnessAnd { constness, value: ty::Binder::bind(t.trait_ref) })
}
PredicateAtom::Projection(..)
| PredicateAtom::Subtype(..)
| PredicateAtom::RegionOutlives(..)
Expand Down Expand Up @@ -1947,7 +1949,7 @@ impl<'tcx> ParamEnv<'tcx> {
}
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TypeFoldable)]
pub struct ConstnessAnd<T> {
pub constness: Constness,
pub value: T,
Expand Down
40 changes: 36 additions & 4 deletions compiler/rustc_mir/src/transform/check_consts/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@ use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorReported};
use rustc_hir::def_id::DefId;
use rustc_hir::{self as hir, HirId, LangItem};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
use rustc_middle::ty::cast::CastTy;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{
self, adjustment::PointerCast, Instance, InstanceDef, Ty, TyCtxt, TypeAndMut,
};
use rustc_middle::ty::{Binder, TraitPredicate, TraitRef};
use rustc_span::{sym, Span, Symbol};
use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
use rustc_trait_selection::traits::{self, TraitEngine};
use rustc_trait_selection::traits::{self, SelectionContext, TraitEngine};

use std::mem;
use std::ops::Deref;
Expand Down Expand Up @@ -765,9 +767,39 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
}
};

// Resolve a trait method call to its concrete implementation, which may be in a
// `const` trait impl.
if self.tcx.features().const_trait_impl {
// Attempting to call a trait method?
if let Some(trait_id) = tcx.trait_of_item(callee) {
if !self.tcx.features().const_trait_impl {
self.check_op(ops::FnCallNonConst(callee));
return;
}

let trait_ref = TraitRef::from_method(tcx, trait_id, substs);
let obligation = Obligation::new(
ObligationCause::dummy(),
param_env,
Binder::bind(TraitPredicate {
trait_ref: TraitRef::from_method(tcx, trait_id, substs),
}),
);

let implsrc = tcx.infer_ctxt().enter(|infcx| {
let mut selcx = SelectionContext::new(&infcx);
selcx.select(&obligation).unwrap()
});

// If the method is provided via a where-clause that does not use the `?const`
// opt-out, the call is allowed.
if let Some(ImplSource::Param(_, hir::Constness::Const)) = implsrc {
debug!(
"const_trait_impl: provided {:?} via where-clause in {:?}",
trait_ref, param_env
);
return;
}

// Resolve a trait method call to its concrete implementation, which may be in a
// `const` trait impl.
let instance = Instance::resolve(tcx, param_env, callee, substs);
debug!("Resolving ({:?}) -> {:?}", callee, instance);
if let Ok(Some(func)) = instance {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -350,11 +350,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {

// Micro-optimization: filter out predicates relating to different traits.
let matching_bounds =
all_bounds.filter(|p| p.def_id() == stack.obligation.predicate.def_id());
all_bounds.filter(|p| p.value.def_id() == stack.obligation.predicate.def_id());

// Keep only those bounds which may apply, and propagate overflow if it occurs.
for bound in matching_bounds {
let wc = self.evaluate_where_clause(stack, bound)?;
let wc = self.evaluate_where_clause(stack, bound.value)?;
if wc.may_apply() {
candidates.vec.push(ParamCandidate(bound));
}
Expand Down
12 changes: 7 additions & 5 deletions compiler/rustc_trait_selection/src/traits/select/confirmation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
//! https://rustc-dev-guide.rust-lang.org/traits/resolution.html#confirmation
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir::lang_items::LangItem;
use rustc_hir::Constness;
use rustc_index::bit_set::GrowableBitSet;
use rustc_infer::infer::InferOk;
use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType;
Expand Down Expand Up @@ -55,8 +56,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}

ParamCandidate(param) => {
let obligations = self.confirm_param_candidate(obligation, param);
Ok(ImplSource::Param(obligations))
let obligations = self.confirm_param_candidate(obligation, param.value);
Ok(ImplSource::Param(obligations, param.constness))
}

ImplCandidate(impl_def_id) => {
Expand All @@ -70,7 +71,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {

ProjectionCandidate(idx) => {
let obligations = self.confirm_projection_candidate(obligation, idx)?;
Ok(ImplSource::Param(obligations))
// FIXME(jschievink): constness
Ok(ImplSource::Param(obligations, Constness::NotConst))
}

ObjectCandidate(idx) => {
Expand Down Expand Up @@ -106,7 +108,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// This indicates something like `Trait + Send: Send`. In this case, we know that
// this holds because that's what the object type is telling us, and there's really
// no additional obligations to prove and no types in particular to unify, etc.
Ok(ImplSource::Param(Vec::new()))
Ok(ImplSource::Param(Vec::new(), Constness::NotConst))
}

BuiltinUnsizeCandidate => {
Expand Down Expand Up @@ -151,7 +153,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligations.extend(self.infcx.commit_if_ok(|_| {
self.infcx
.at(&obligation.cause, obligation.param_env)
.sup(placeholder_trait_predicate.trait_ref.to_poly_trait_ref(), candidate)
.sup(placeholder_trait_predicate.trait_ref.to_poly_trait_ref(), candidate.value)
.map(|InferOk { obligations, .. }| obligations)
.map_err(|_| Unimplemented)
})?);
Expand Down
16 changes: 12 additions & 4 deletions compiler/rustc_trait_selection/src/traits/select/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::ErrorReported;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::Constness;
use rustc_middle::dep_graph::{DepKind, DepNodeIndex};
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::ty::fast_reject;
Expand Down Expand Up @@ -1335,7 +1336,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
(BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate, _) => true,
(_, BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate) => false,

(ParamCandidate(..), ParamCandidate(..)) => false,
(ParamCandidate(other), ParamCandidate(victim)) => {
if other.value == victim.value && victim.constness == Constness::NotConst {
// Drop otherwise equivalent non-const candidates in favor of const candidates.
true
} else {
false
}
}

// Global bounds from the where clause should be ignored
// here (see issue #50825). Otherwise, we have a where
Expand All @@ -1354,11 +1362,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
| TraitAliasCandidate(..)
| ObjectCandidate(_)
| ProjectionCandidate(_),
) => !is_global(cand),
) => !is_global(&cand.value),
(ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(ref cand)) => {
// Prefer these to a global where-clause bound
// (see issue #50825).
is_global(cand)
is_global(&cand.value)
}
(
ImplCandidate(_)
Expand All @@ -1373,7 +1381,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
) => {
// Prefer these to a global where-clause bound
// (see issue #50825).
is_global(cand) && other.evaluation.must_apply_modulo_regions()
is_global(&cand.value) && other.evaluation.must_apply_modulo_regions()
}

(ProjectionCandidate(i), ProjectionCandidate(j))
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_trait_selection/src/traits/specialize/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -498,8 +498,8 @@ fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option<String>

for (p, _) in predicates {
if let Some(poly_trait_ref) = p.to_opt_poly_trait_ref() {
if Some(poly_trait_ref.def_id()) == sized_trait {
types_without_default_bounds.remove(poly_trait_ref.self_ty().skip_binder());
if Some(poly_trait_ref.value.def_id()) == sized_trait {
types_without_default_bounds.remove(poly_trait_ref.value.self_ty().skip_binder());
continue;
}
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_trait_selection/src/traits/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ impl<'tcx> TraitAliasExpander<'tcx> {
let items = predicates.predicates.iter().rev().filter_map(|(pred, span)| {
pred.subst_supertrait(tcx, &trait_ref)
.to_opt_poly_trait_ref()
.map(|trait_ref| item.clone_and_push(trait_ref, *span))
.map(|trait_ref| item.clone_and_push(trait_ref.value, *span))
});
debug!("expand_trait_aliases: items={:?}", items.clone());

Expand Down Expand Up @@ -182,7 +182,7 @@ impl Iterator for SupertraitDefIds<'tcx> {
.predicates
.iter()
.filter_map(|(pred, _)| pred.to_opt_poly_trait_ref())
.map(|trait_ref| trait_ref.def_id())
.map(|trait_ref| trait_ref.value.def_id())
.filter(|&super_def_id| visited.insert(super_def_id)),
);
Some(def_id)
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_trait_selection/src/traits/wf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
let mut cause = cause.clone();
if let Some(parent_trait_ref) = obligation.predicate.to_opt_poly_trait_ref() {
let derived_cause = traits::DerivedObligationCause {
parent_trait_ref,
parent_trait_ref: parent_trait_ref.value,
parent_code: Rc::new(obligation.cause.code.clone()),
};
cause.make_mut().code =
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_typeck/src/astconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1364,7 +1364,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|| {
traits::transitive_bounds(
tcx,
predicates.iter().filter_map(|(p, _)| p.to_opt_poly_trait_ref()),
predicates.iter().filter_map(|(p, _)| {
p.to_opt_poly_trait_ref().map(|trait_ref| trait_ref.value)
}),
)
},
|| param_name.to_string(),
Expand Down
11 changes: 11 additions & 0 deletions src/test/ui/rfc-2632-const-trait-impl/call-generic-method-fail.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#![feature(const_fn)]
#![feature(const_trait_impl)]
#![feature(const_trait_bound_opt_out)]
#![allow(incomplete_features)]

pub const fn equals_self<T: ?const PartialEq>(t: &T) -> bool {
*t == *t
//~^ ERROR calls in constant functions are limited to constant functions
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
--> $DIR/call-generic-method-fail.rs:7:5
|
LL | *t == *t
| ^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0015`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// check-pass

#![feature(const_fn)]
#![feature(const_trait_impl)]
#![feature(const_trait_bound_opt_out)]
#![allow(incomplete_features)]

struct S;

impl PartialEq for S {
fn eq(&self, _: &S) -> bool {
true
}
}

const fn equals_self<T: ?const PartialEq>(t: &T) -> bool {
true
}

pub const EQ: bool = equals_self(&S);

// Calling `equals_self` with a type that only has a non-const impl is fine, because we opted out.

fn main() {}
Loading