Skip to content

Commit

Permalink
Auto merge of #100982 - fee1-dead-contrib:const-impl-requires-const-t…
Browse files Browse the repository at this point in the history
…rait, r=oli-obk

Require `#[const_trait]` on `Trait` for `impl const Trait`

r? `@oli-obk`
  • Loading branch information
bors committed Sep 22, 2022
2 parents 626b02a + 898c76c commit 7a8636c
Show file tree
Hide file tree
Showing 80 changed files with 432 additions and 244 deletions.
6 changes: 6 additions & 0 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,12 +428,18 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
}

if let ty::FnDef(def_id, substs) = *constant.literal.ty().kind() {
// const_trait_impl: use a non-const param env when checking that a FnDef type is well formed.
// this is because the well-formedness of the function does not need to be proved to have `const`
// impls for trait bounds.
let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs);
let prev = self.cx.param_env;
self.cx.param_env = prev.without_const();
self.cx.normalize_and_prove_instantiated_predicates(
def_id,
instantiated_predicates,
locations,
);
self.cx.param_env = prev;
}
}
}
Expand Down
44 changes: 42 additions & 2 deletions compiler/rustc_const_eval/src/transform/check_consts/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ use rustc_middle::ty::{self, adjustment::PointerCast, Instance, InstanceDef, Ty,
use rustc_middle::ty::{Binder, TraitPredicate, TraitRef, TypeVisitable};
use rustc_mir_dataflow::{self, Analysis};
use rustc_span::{sym, Span, Symbol};
use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
use rustc_trait_selection::traits::SelectionContext;
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
use rustc_trait_selection::traits::{
self, ObligationCauseCode, SelectionContext, TraitEngine, TraitEngineExt,
};

use std::mem;
use std::ops::Deref;
Expand Down Expand Up @@ -739,6 +742,43 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
selcx.select(&obligation)
});

// do a well-formedness check on the trait method being called. This is because typeck only does a
// "non-const" check. This is required for correctness here.
tcx.infer_ctxt().enter(|infcx| {
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
let predicates = tcx.predicates_of(callee).instantiate(tcx, substs);
let hir_id = tcx
.hir()
.local_def_id_to_hir_id(self.body.source.def_id().expect_local());
let cause = || {
ObligationCause::new(
terminator.source_info.span,
hir_id,
ObligationCauseCode::ItemObligation(callee),
)
};
let normalized = infcx.partially_normalize_associated_types_in(
cause(),
param_env,
predicates,
);

for p in normalized.obligations {
fulfill_cx.register_predicate_obligation(&infcx, p);
}
for obligation in traits::predicates_for_generics(
|_, _| cause(),
self.param_env,
normalized.value,
) {
fulfill_cx.register_predicate_obligation(&infcx, obligation);
}
let errors = fulfill_cx.select_all_or_error(&infcx);
if !errors.is_empty() {
infcx.report_fulfillment_errors(&errors, None, false);
}
});

match implsrc {
Ok(Some(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => {
debug!(
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
// RFC 2632
gated!(
const_trait, Normal, template!(Word), WarnFollowing, const_trait_impl,
"`const` is a temporary placeholder for marking a trait that is suitable for `const` \
"`const_trait` is a temporary placeholder for marking a trait that is suitable for `const` \
`impls` and all default bodies as `const`, which may be removed or renamed in the \
future."
),
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_infer/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ impl<'tcx> PredicateObligation<'tcx> {
recursion_depth: self.recursion_depth,
})
}

pub fn without_const(mut self, tcx: TyCtxt<'tcx>) -> PredicateObligation<'tcx> {
self.param_env = self.param_env.without_const();
if let ty::PredicateKind::Trait(trait_pred) = self.predicate.kind().skip_binder() && trait_pred.is_const_if_const() {
self.predicate = tcx.mk_predicate(self.predicate.kind().map_bound(|_| ty::PredicateKind::Trait(trait_pred.without_const())));
}
self
}
}

impl<'tcx> TraitObligation<'tcx> {
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,11 @@ impl<'tcx> TraitPredicate<'tcx> {
(BoundConstness::ConstIfConst, hir::Constness::NotConst) => false,
}
}

pub fn without_const(mut self) -> Self {
self.constness = BoundConstness::NotConst;
self
}
}

impl<'tcx> PolyTraitPredicate<'tcx> {
Expand Down
22 changes: 22 additions & 0 deletions compiler/rustc_passes/src/check_const.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,28 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> {
}

fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
let tcx = self.tcx;
if let hir::ItemKind::Impl(hir::Impl {
constness: hir::Constness::Const,
of_trait: Some(trait_ref),
..
}) = item.kind
{
let def_id = trait_ref.trait_def_id().unwrap();
let source_map = tcx.sess.source_map();
if !tcx.has_attr(def_id, sym::const_trait) {
tcx.sess
.struct_span_err(
source_map.guess_head_span(item.span),
"const `impl`s must be for traits marked with `#[const_trait]`",
)
.span_note(
source_map.guess_head_span(tcx.def_span(def_id)),
"this trait must be annotated with `#[const_trait]`",
)
.emit();
}
}
intravisit::walk_item(self, item);
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_trait_selection/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ pub fn predicates_for_generics<'tcx>(
move |(idx, (predicate, span))| Obligation {
cause: cause(idx, span),
recursion_depth: 0,
param_env: param_env,
param_env,
predicate,
},
)
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_trait_selection/src/traits/wf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,8 @@ impl<'tcx> WfPredicates<'tcx> {
// `i32: Clone`
// `i32: Copy`
// ]
let obligations = self.nominal_obligations(data.item_def_id, data.substs);
// Projection types do not require const predicates.
let obligations = self.nominal_obligations_without_const(data.item_def_id, data.substs);
self.out.extend(obligations);

let tcx = self.tcx();
Expand Down
33 changes: 30 additions & 3 deletions compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1406,23 +1406,50 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
})
}

#[instrument(level = "debug", skip(self, code, span, def_id, substs))]
#[instrument(level = "debug", skip(self, code, span, substs))]
fn add_required_obligations_with_code(
&self,
span: Span,
def_id: DefId,
substs: SubstsRef<'tcx>,
code: impl Fn(usize, Span) -> ObligationCauseCode<'tcx>,
) {
let param_env = self.param_env;

let remap = match self.tcx.def_kind(def_id) {
// Associated consts have `Self: ~const Trait` bounds that should be satisfiable when
// `Self: Trait` is satisfied because it does not matter whether the impl is `const`.
// Therefore we have to remap the param env here to be non-const.
hir::def::DefKind::AssocConst => true,
hir::def::DefKind::AssocFn
if self.tcx.def_kind(self.tcx.parent(def_id)) == hir::def::DefKind::Trait =>
{
// N.B.: All callsites to this function involve checking a path expression.
//
// When instantiating a trait method as a function item, it does not actually matter whether
// the trait is `const` or not, or whether `where T: ~const Tr` needs to be satisfied as
// `const`. If we were to introduce instantiating trait methods as `const fn`s, we would
// check that after this, either via a bound `where F: ~const FnOnce` or when coercing to a
// `const fn` pointer.
//
// FIXME(fee1-dead) FIXME(const_trait_impl): update this doc when trait methods can satisfy
// `~const FnOnce` or can be coerced to `const fn` pointer.
true
}
_ => false,
};
let (bounds, _) = self.instantiate_bounds(span, def_id, &substs);

for obligation in traits::predicates_for_generics(
for mut obligation in traits::predicates_for_generics(
|idx, predicate_span| {
traits::ObligationCause::new(span, self.body_id, code(idx, predicate_span))
},
self.param_env,
param_env,
bounds,
) {
if remap {
obligation = obligation.without_const(self.tcx);
}
self.register_predicate(obligation);
}
}
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_typeck/src/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ fn typeck_with_fallback<'tcx>(

let typeck_results = Inherited::build(tcx, def_id).enter(|inh| {
let param_env = tcx.param_env(def_id);
let fcx = if let Some(hir::FnSig { header, decl, .. }) = fn_sig {
let mut fcx = if let Some(hir::FnSig { header, decl, .. }) = fn_sig {
let fn_sig = if crate::collect::get_infer_ret_ty(&decl.output).is_some() {
let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id);
<dyn AstConv<'_>>::ty_of_fn(&fcx, id, header.unsafety, header.abi, decl, None, None)
Expand Down Expand Up @@ -459,7 +459,11 @@ fn typeck_with_fallback<'tcx>(

// Closure and generator analysis may run after fallback
// because they don't constrain other type variables.
// Closure analysis only runs on closures. Therefore they only need to fulfill non-const predicates (as of now)
let prev_constness = fcx.param_env.constness();
fcx.param_env = fcx.param_env.without_const();
fcx.closure_analyze(body);
fcx.param_env = fcx.param_env.with_constness(prev_constness);
assert!(fcx.deferred_call_resolutions.borrow().is_empty());
// Before the generator analysis, temporary scopes shall be marked to provide more
// precise information on types to be captured.
Expand Down
3 changes: 1 addition & 2 deletions library/alloc/src/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2037,8 +2037,7 @@ impl<T: ?Sized, A: Allocator> AsMut<T> for Box<T, A> {
* could have a method to project a Pin<T> from it.
*/
#[stable(feature = "pin", since = "1.33.0")]
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
impl<T: ?Sized, A: Allocator> const Unpin for Box<T, A> where A: 'static {}
impl<T: ?Sized, A: Allocator> Unpin for Box<T, A> where A: 'static {}

#[unstable(feature = "generator_trait", issue = "43122")]
impl<G: ?Sized + Generator<R> + Unpin, R, A: Allocator> Generator<R> for Box<G, A>
Expand Down
1 change: 1 addition & 0 deletions library/core/src/alloc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ impl fmt::Display for AllocError {
///
/// [*currently allocated*]: #currently-allocated-memory
#[unstable(feature = "allocator_api", issue = "32838")]
#[const_trait]
pub unsafe trait Allocator {
/// Attempts to allocate a block of memory.
///
Expand Down
2 changes: 2 additions & 0 deletions library/core/src/borrow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@
/// [`String`]: ../../std/string/struct.String.html
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_diagnostic_item = "Borrow"]
#[const_trait]
pub trait Borrow<Borrowed: ?Sized> {
/// Immutably borrows from an owned value.
///
Expand Down Expand Up @@ -184,6 +185,7 @@ pub trait Borrow<Borrowed: ?Sized> {
/// an underlying type by providing a mutable reference. See [`Borrow<T>`]
/// for more information on borrowing as another type.
#[stable(feature = "rust1", since = "1.0.0")]
#[const_trait]
pub trait BorrowMut<Borrowed: ?Sized>: Borrow<Borrowed> {
/// Mutably borrows from an owned value.
///
Expand Down
36 changes: 8 additions & 28 deletions library/core/src/cmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,20 +204,10 @@ use self::Ordering::*;
#[stable(feature = "rust1", since = "1.0.0")]
#[doc(alias = "==")]
#[doc(alias = "!=")]
#[cfg_attr(
bootstrap,
rustc_on_unimplemented(
message = "can't compare `{Self}` with `{Rhs}`",
label = "no implementation for `{Self} == {Rhs}`"
)
)]
#[cfg_attr(
not(bootstrap),
rustc_on_unimplemented(
message = "can't compare `{Self}` with `{Rhs}`",
label = "no implementation for `{Self} == {Rhs}`",
append_const_msg,
)
#[rustc_on_unimplemented(
message = "can't compare `{Self}` with `{Rhs}`",
label = "no implementation for `{Self} == {Rhs}`",
append_const_msg
)]
#[const_trait]
#[rustc_diagnostic_item = "PartialEq"]
Expand Down Expand Up @@ -1076,20 +1066,10 @@ impl const PartialOrd for Ordering {
#[doc(alias = "<")]
#[doc(alias = "<=")]
#[doc(alias = ">=")]
#[cfg_attr(
bootstrap,
rustc_on_unimplemented(
message = "can't compare `{Self}` with `{Rhs}`",
label = "no implementation for `{Self} < {Rhs}` and `{Self} > {Rhs}`",
)
)]
#[cfg_attr(
not(bootstrap),
rustc_on_unimplemented(
message = "can't compare `{Self}` with `{Rhs}`",
label = "no implementation for `{Self} < {Rhs}` and `{Self} > {Rhs}`",
append_const_msg,
)
#[rustc_on_unimplemented(
message = "can't compare `{Self}` with `{Rhs}`",
label = "no implementation for `{Self} < {Rhs}` and `{Self} > {Rhs}`",
append_const_msg
)]
#[const_trait]
#[rustc_diagnostic_item = "PartialOrd"]
Expand Down
6 changes: 6 additions & 0 deletions library/core/src/convert/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ pub const fn identity<T>(x: T) -> T {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "AsRef")]
#[const_trait]
pub trait AsRef<T: ?Sized> {
/// Converts this type into a shared reference of the (usually inferred) input type.
#[stable(feature = "rust1", since = "1.0.0")]
Expand Down Expand Up @@ -197,6 +198,7 @@ pub trait AsRef<T: ?Sized> {
/// [`Box<T>`]: ../../std/boxed/struct.Box.html
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "AsMut")]
#[const_trait]
pub trait AsMut<T: ?Sized> {
/// Converts this type into a mutable reference of the (usually inferred) input type.
#[stable(feature = "rust1", since = "1.0.0")]
Expand Down Expand Up @@ -273,6 +275,7 @@ pub trait AsMut<T: ?Sized> {
/// [`Vec`]: ../../std/vec/struct.Vec.html
#[rustc_diagnostic_item = "Into"]
#[stable(feature = "rust1", since = "1.0.0")]
#[const_trait]
pub trait Into<T>: Sized {
/// Converts this type into the (usually inferred) input type.
#[must_use]
Expand Down Expand Up @@ -368,6 +371,7 @@ pub trait Into<T>: Sized {
all(_Self = "&str", T = "std::string::String"),
note = "to coerce a `{T}` into a `{Self}`, use `&*` as a prefix",
))]
#[const_trait]
pub trait From<T>: Sized {
/// Converts to this type from the input type.
#[lang = "from"]
Expand All @@ -392,6 +396,7 @@ pub trait From<T>: Sized {
/// [`Into`], see there for details.
#[rustc_diagnostic_item = "TryInto"]
#[stable(feature = "try_from", since = "1.34.0")]
#[const_trait]
pub trait TryInto<T>: Sized {
/// The type returned in the event of a conversion error.
#[stable(feature = "try_from", since = "1.34.0")]
Expand Down Expand Up @@ -468,6 +473,7 @@ pub trait TryInto<T>: Sized {
/// [`try_from`]: TryFrom::try_from
#[rustc_diagnostic_item = "TryFrom"]
#[stable(feature = "try_from", since = "1.34.0")]
#[const_trait]
pub trait TryFrom<T>: Sized {
/// The type returned in the event of a conversion error.
#[stable(feature = "try_from", since = "1.34.0")]
Expand Down
1 change: 1 addition & 0 deletions library/core/src/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
/// ```
#[cfg_attr(not(test), rustc_diagnostic_item = "Default")]
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(bootstrap), const_trait)]
pub trait Default: Sized {
/// Returns the "default value" for a type.
///
Expand Down
1 change: 1 addition & 0 deletions library/core/src/iter/traits/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ pub trait FromIterator<A>: Sized {
#[rustc_diagnostic_item = "IntoIterator"]
#[rustc_skip_array_during_method_dispatch]
#[stable(feature = "rust1", since = "1.0.0")]
#[const_trait]
pub trait IntoIterator {
/// The type of the elements being iterated over.
#[stable(feature = "rust1", since = "1.0.0")]
Expand Down
1 change: 1 addition & 0 deletions library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@
#![feature(const_str_from_utf8_unchecked_mut)]
#![feature(const_swap)]
#![feature(const_trait_impl)]
#![feature(const_try)]
#![feature(const_type_id)]
#![feature(const_type_name)]
#![feature(const_default_impls)]
Expand Down
Loading

0 comments on commit 7a8636c

Please sign in to comment.