Skip to content

Commit 6af388b

Browse files
committed
Auto merge of #68847 - ecstatic-morse:const-impl, r=oli-obk
Allow trait methods to be called on concrete types in a const context This partially implements [RFC 2632](rust-lang/rfcs#2632) by const-checking methods inside an `impl const` block and allowing those methods to be called on concrete types. Calling trait methods on type parameters in a const context is not yet allowed. Implementing this will require much more work. Since we are only concerned with methods on concrete types, we are able to take advantage of the machinery in `Instance::resolve`, which is doing most of the work. This also propagates `#[rustc_const_unstable]` from parent items to child items, making that attribute behave like `#[stable]` and `#[unstable]` do. This allows trait methods to be marked as unstably const. cc #67792 #57563 cc @rust-lang/wg-const-eval r? @oli-obk
2 parents de362d8 + 19801b1 commit 6af388b

22 files changed

+597
-221
lines changed

src/librustc/query/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,14 @@ rustc_queries! {
279279
desc { |tcx| "checking if item is const fn: `{}`", tcx.def_path_str(key) }
280280
}
281281

282+
/// Returns `true` if this is a const `impl`. **Do not call this function manually.**
283+
///
284+
/// This query caches the base data for the `is_const_impl` helper function, which also
285+
/// takes into account stability attributes (e.g., `#[rustc_const_unstable]`).
286+
query is_const_impl_raw(key: DefId) -> bool {
287+
desc { |tcx| "checking if item is const impl: `{}`", tcx.def_path_str(key) }
288+
}
289+
282290
query asyncness(key: DefId) -> hir::IsAsync {
283291
desc { |tcx| "checking if the function is async: `{}`", tcx.def_path_str(key) }
284292
}

src/librustc_ast_lowering/item.rs

+1-9
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,7 @@ impl<'a> Visitor<'a> for ItemLowerer<'a, '_, '_> {
6666
if let Some(hir_id) = item_hir_id {
6767
self.lctx.with_parent_item_lifetime_defs(hir_id, |this| {
6868
let this = &mut ItemLowerer { lctx: this };
69-
if let ItemKind::Impl { constness, ref of_trait, .. } = item.kind {
70-
if let Const::Yes(span) = constness {
71-
this.lctx
72-
.diagnostic()
73-
.struct_span_err(item.span, "const trait impls are not yet implemented")
74-
.span_label(span, "const because of this")
75-
.emit();
76-
}
77-
69+
if let ItemKind::Impl { ref of_trait, .. } = item.kind {
7870
this.with_trait_impl_ref(of_trait, |this| visit::walk_item(this, item));
7971
} else {
8072
visit::walk_item(this, item);

src/librustc_mir/const_eval/fn_queries.rs

+77-53
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use rustc::ty::query::Providers;
33
use rustc::ty::TyCtxt;
44
use rustc_attr as attr;
55
use rustc_hir as hir;
6-
use rustc_hir::def_id::DefId;
6+
use rustc_hir::def_id::{DefId, LocalDefId};
77
use rustc_span::symbol::Symbol;
88
use rustc_target::spec::abi::Abi;
99

@@ -82,72 +82,96 @@ pub fn is_min_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
8282
}
8383
}
8484

85-
pub fn provide(providers: &mut Providers<'_>) {
86-
/// Const evaluability whitelist is here to check evaluability at the
87-
/// top level beforehand.
88-
fn is_const_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> Option<bool> {
89-
if tcx.is_closure(def_id) {
90-
return None;
91-
}
85+
pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool {
86+
let parent_id = tcx.hir().get_parent_did(hir_id);
87+
if !parent_id.is_top_level_module() {
88+
is_const_impl_raw(tcx, LocalDefId::from_def_id(parent_id))
89+
} else {
90+
false
91+
}
92+
}
9293

93-
match tcx.fn_sig(def_id).abi() {
94-
Abi::RustIntrinsic | Abi::PlatformIntrinsic => {
95-
Some(tcx.lookup_const_stability(def_id).is_some())
96-
}
97-
_ => None,
94+
/// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether
95+
/// said intrinsic is on the whitelist for being const callable.
96+
fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
97+
let hir_id =
98+
tcx.hir().as_local_hir_id(def_id).expect("Non-local call to local provider is_const_fn");
99+
100+
let node = tcx.hir().get(hir_id);
101+
102+
if let Some(whitelisted) = is_const_intrinsic(tcx, def_id) {
103+
whitelisted
104+
} else if let Some(fn_like) = FnLikeNode::from_node(node) {
105+
if fn_like.constness() == hir::Constness::Const {
106+
return true;
98107
}
99-
}
100108

101-
/// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether
102-
/// said intrinsic is on the whitelist for being const callable.
103-
fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
104-
let hir_id = tcx
105-
.hir()
106-
.as_local_hir_id(def_id)
107-
.expect("Non-local call to local provider is_const_fn");
109+
// If the function itself is not annotated with `const`, it may still be a `const fn`
110+
// if it resides in a const trait impl.
111+
is_parent_const_impl_raw(tcx, hir_id)
112+
} else if let hir::Node::Ctor(_) = node {
113+
true
114+
} else {
115+
false
116+
}
117+
}
108118

109-
let node = tcx.hir().get(hir_id);
119+
/// Const evaluability whitelist is here to check evaluability at the
120+
/// top level beforehand.
121+
fn is_const_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> Option<bool> {
122+
if tcx.is_closure(def_id) {
123+
return None;
124+
}
110125

111-
if let Some(whitelisted) = is_const_intrinsic(tcx, def_id) {
112-
whitelisted
113-
} else if let Some(fn_like) = FnLikeNode::from_node(node) {
114-
fn_like.constness() == hir::Constness::Const
115-
} else if let hir::Node::Ctor(_) = node {
116-
true
117-
} else {
118-
false
126+
match tcx.fn_sig(def_id).abi() {
127+
Abi::RustIntrinsic | Abi::PlatformIntrinsic => {
128+
Some(tcx.lookup_const_stability(def_id).is_some())
119129
}
130+
_ => None,
120131
}
132+
}
121133

122-
fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
123-
is_const_fn(tcx, def_id)
124-
&& match tcx.lookup_const_stability(def_id) {
125-
Some(stab) => {
126-
if cfg!(debug_assertions) && stab.promotable {
127-
let sig = tcx.fn_sig(def_id);
128-
assert_eq!(
129-
sig.unsafety(),
130-
hir::Unsafety::Normal,
131-
"don't mark const unsafe fns as promotable",
132-
// https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682
133-
);
134-
}
135-
stab.promotable
134+
/// Checks whether the given item is an `impl` that has a `const` modifier.
135+
fn is_const_impl_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
136+
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
137+
let node = tcx.hir().get(hir_id);
138+
matches!(
139+
node,
140+
hir::Node::Item(hir::Item {
141+
kind: hir::ItemKind::Impl { constness: hir::Constness::Const, .. },
142+
..
143+
})
144+
)
145+
}
146+
147+
fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
148+
is_const_fn(tcx, def_id)
149+
&& match tcx.lookup_const_stability(def_id) {
150+
Some(stab) => {
151+
if cfg!(debug_assertions) && stab.promotable {
152+
let sig = tcx.fn_sig(def_id);
153+
assert_eq!(
154+
sig.unsafety(),
155+
hir::Unsafety::Normal,
156+
"don't mark const unsafe fns as promotable",
157+
// https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682
158+
);
136159
}
137-
None => false,
160+
stab.promotable
138161
}
139-
}
162+
None => false,
163+
}
164+
}
140165

141-
fn const_fn_is_allowed_fn_ptr(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
142-
is_const_fn(tcx, def_id)
143-
&& tcx
144-
.lookup_const_stability(def_id)
145-
.map(|stab| stab.allow_const_fn_ptr)
146-
.unwrap_or(false)
147-
}
166+
fn const_fn_is_allowed_fn_ptr(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
167+
is_const_fn(tcx, def_id)
168+
&& tcx.lookup_const_stability(def_id).map(|stab| stab.allow_const_fn_ptr).unwrap_or(false)
169+
}
148170

171+
pub fn provide(providers: &mut Providers<'_>) {
149172
*providers = Providers {
150173
is_const_fn_raw,
174+
is_const_impl_raw: |tcx, def_id| is_const_impl_raw(tcx, LocalDefId::from_def_id(def_id)),
151175
is_promotable_const_fn,
152176
const_fn_is_allowed_fn_ptr,
153177
..*providers

src/librustc_mir/transform/check_consts/validation.rs

+17-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use rustc::middle::lang_items;
44
use rustc::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
55
use rustc::mir::*;
66
use rustc::ty::cast::CastTy;
7-
use rustc::ty::{self, TyCtxt};
7+
use rustc::ty::{self, Instance, InstanceDef, TyCtxt};
88
use rustc_errors::struct_span_err;
99
use rustc_hir::{def_id::DefId, HirId};
1010
use rustc_index::bit_set::BitSet;
@@ -502,8 +502,8 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
502502
TerminatorKind::Call { func, .. } => {
503503
let fn_ty = func.ty(*self.body, self.tcx);
504504

505-
let def_id = match fn_ty.kind {
506-
ty::FnDef(def_id, _) => def_id,
505+
let (def_id, substs) = match fn_ty.kind {
506+
ty::FnDef(def_id, substs) => (def_id, substs),
507507

508508
ty::FnPtr(_) => {
509509
self.check_op(ops::FnCallIndirect);
@@ -520,6 +520,20 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
520520
return;
521521
}
522522

523+
// See if this is a trait method for a concrete type whose impl of that trait is
524+
// `const`.
525+
if self.tcx.features().const_trait_impl {
526+
let instance = Instance::resolve(self.tcx, self.param_env, def_id, substs);
527+
debug!("Resolving ({:?}) -> {:?}", def_id, instance);
528+
if let Some(func) = instance {
529+
if let InstanceDef::Item(def_id) = func.def {
530+
if is_const_fn(self.tcx, def_id) {
531+
return;
532+
}
533+
}
534+
}
535+
}
536+
523537
if is_lang_panic_fn(self.tcx, def_id) {
524538
self.check_op(ops::Panic);
525539
} else if let Some(feature) = is_unstable_const_fn(self.tcx, def_id) {

src/librustc_mir/transform/qualify_min_const_fn.rs

+8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ use std::borrow::Cow;
1010
type McfResult = Result<(), (Span, Cow<'static, str>)>;
1111

1212
pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) -> McfResult {
13+
// Prevent const trait methods from being annotated as `stable`.
14+
if tcx.features().staged_api {
15+
let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
16+
if crate::const_eval::is_parent_const_impl_raw(tcx, hir_id) {
17+
return Err((body.span, "trait methods cannot be stable const fn".into()));
18+
}
19+
}
20+
1321
let mut current = def_id;
1422
loop {
1523
let predicates = tcx.predicates_of(current);

0 commit comments

Comments
 (0)