Skip to content

Commit 7a8636c

Browse files
committed
Auto merge of rust-lang#100982 - fee1-dead-contrib:const-impl-requires-const-trait, r=oli-obk
Require `#[const_trait]` on `Trait` for `impl const Trait` r? `@oli-obk`
2 parents 626b02a + 898c76c commit 7a8636c

File tree

80 files changed

+432
-244
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+432
-244
lines changed

compiler/rustc_borrowck/src/type_check/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,12 +428,18 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
428428
}
429429

430430
if let ty::FnDef(def_id, substs) = *constant.literal.ty().kind() {
431+
// const_trait_impl: use a non-const param env when checking that a FnDef type is well formed.
432+
// this is because the well-formedness of the function does not need to be proved to have `const`
433+
// impls for trait bounds.
431434
let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs);
435+
let prev = self.cx.param_env;
436+
self.cx.param_env = prev.without_const();
432437
self.cx.normalize_and_prove_instantiated_predicates(
433438
def_id,
434439
instantiated_predicates,
435440
locations,
436441
);
442+
self.cx.param_env = prev;
437443
}
438444
}
439445
}

compiler/rustc_const_eval/src/transform/check_consts/check.rs

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@ use rustc_middle::ty::{self, adjustment::PointerCast, Instance, InstanceDef, Ty,
1313
use rustc_middle::ty::{Binder, TraitPredicate, TraitRef, TypeVisitable};
1414
use rustc_mir_dataflow::{self, Analysis};
1515
use rustc_span::{sym, Span, Symbol};
16-
use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
17-
use rustc_trait_selection::traits::SelectionContext;
16+
use rustc_trait_selection::infer::InferCtxtExt;
17+
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
18+
use rustc_trait_selection::traits::{
19+
self, ObligationCauseCode, SelectionContext, TraitEngine, TraitEngineExt,
20+
};
1821

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

745+
// do a well-formedness check on the trait method being called. This is because typeck only does a
746+
// "non-const" check. This is required for correctness here.
747+
tcx.infer_ctxt().enter(|infcx| {
748+
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
749+
let predicates = tcx.predicates_of(callee).instantiate(tcx, substs);
750+
let hir_id = tcx
751+
.hir()
752+
.local_def_id_to_hir_id(self.body.source.def_id().expect_local());
753+
let cause = || {
754+
ObligationCause::new(
755+
terminator.source_info.span,
756+
hir_id,
757+
ObligationCauseCode::ItemObligation(callee),
758+
)
759+
};
760+
let normalized = infcx.partially_normalize_associated_types_in(
761+
cause(),
762+
param_env,
763+
predicates,
764+
);
765+
766+
for p in normalized.obligations {
767+
fulfill_cx.register_predicate_obligation(&infcx, p);
768+
}
769+
for obligation in traits::predicates_for_generics(
770+
|_, _| cause(),
771+
self.param_env,
772+
normalized.value,
773+
) {
774+
fulfill_cx.register_predicate_obligation(&infcx, obligation);
775+
}
776+
let errors = fulfill_cx.select_all_or_error(&infcx);
777+
if !errors.is_empty() {
778+
infcx.report_fulfillment_errors(&errors, None, false);
779+
}
780+
});
781+
742782
match implsrc {
743783
Ok(Some(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => {
744784
debug!(

compiler/rustc_feature/src/builtin_attrs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
468468
// RFC 2632
469469
gated!(
470470
const_trait, Normal, template!(Word), WarnFollowing, const_trait_impl,
471-
"`const` is a temporary placeholder for marking a trait that is suitable for `const` \
471+
"`const_trait` is a temporary placeholder for marking a trait that is suitable for `const` \
472472
`impls` and all default bodies as `const`, which may be removed or renamed in the \
473473
future."
474474
),

compiler/rustc_infer/src/traits/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,14 @@ impl<'tcx> PredicateObligation<'tcx> {
6767
recursion_depth: self.recursion_depth,
6868
})
6969
}
70+
71+
pub fn without_const(mut self, tcx: TyCtxt<'tcx>) -> PredicateObligation<'tcx> {
72+
self.param_env = self.param_env.without_const();
73+
if let ty::PredicateKind::Trait(trait_pred) = self.predicate.kind().skip_binder() && trait_pred.is_const_if_const() {
74+
self.predicate = tcx.mk_predicate(self.predicate.kind().map_bound(|_| ty::PredicateKind::Trait(trait_pred.without_const())));
75+
}
76+
self
77+
}
7078
}
7179

7280
impl<'tcx> TraitObligation<'tcx> {

compiler/rustc_middle/src/ty/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,11 @@ impl<'tcx> TraitPredicate<'tcx> {
861861
(BoundConstness::ConstIfConst, hir::Constness::NotConst) => false,
862862
}
863863
}
864+
865+
pub fn without_const(mut self) -> Self {
866+
self.constness = BoundConstness::NotConst;
867+
self
868+
}
864869
}
865870

866871
impl<'tcx> PolyTraitPredicate<'tcx> {

compiler/rustc_passes/src/check_const.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,28 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> {
192192
}
193193

194194
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
195+
let tcx = self.tcx;
196+
if let hir::ItemKind::Impl(hir::Impl {
197+
constness: hir::Constness::Const,
198+
of_trait: Some(trait_ref),
199+
..
200+
}) = item.kind
201+
{
202+
let def_id = trait_ref.trait_def_id().unwrap();
203+
let source_map = tcx.sess.source_map();
204+
if !tcx.has_attr(def_id, sym::const_trait) {
205+
tcx.sess
206+
.struct_span_err(
207+
source_map.guess_head_span(item.span),
208+
"const `impl`s must be for traits marked with `#[const_trait]`",
209+
)
210+
.span_note(
211+
source_map.guess_head_span(tcx.def_span(def_id)),
212+
"this trait must be annotated with `#[const_trait]`",
213+
)
214+
.emit();
215+
}
216+
}
195217
intravisit::walk_item(self, item);
196218
}
197219

compiler/rustc_trait_selection/src/traits/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ pub fn predicates_for_generics<'tcx>(
129129
move |(idx, (predicate, span))| Obligation {
130130
cause: cause(idx, span),
131131
recursion_depth: 0,
132-
param_env: param_env,
132+
param_env,
133133
predicate,
134134
},
135135
)

compiler/rustc_trait_selection/src/traits/wf.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,8 @@ impl<'tcx> WfPredicates<'tcx> {
392392
// `i32: Clone`
393393
// `i32: Copy`
394394
// ]
395-
let obligations = self.nominal_obligations(data.item_def_id, data.substs);
395+
// Projection types do not require const predicates.
396+
let obligations = self.nominal_obligations_without_const(data.item_def_id, data.substs);
396397
self.out.extend(obligations);
397398

398399
let tcx = self.tcx();

compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1406,23 +1406,50 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14061406
})
14071407
}
14081408

1409-
#[instrument(level = "debug", skip(self, code, span, def_id, substs))]
1409+
#[instrument(level = "debug", skip(self, code, span, substs))]
14101410
fn add_required_obligations_with_code(
14111411
&self,
14121412
span: Span,
14131413
def_id: DefId,
14141414
substs: SubstsRef<'tcx>,
14151415
code: impl Fn(usize, Span) -> ObligationCauseCode<'tcx>,
14161416
) {
1417+
let param_env = self.param_env;
1418+
1419+
let remap = match self.tcx.def_kind(def_id) {
1420+
// Associated consts have `Self: ~const Trait` bounds that should be satisfiable when
1421+
// `Self: Trait` is satisfied because it does not matter whether the impl is `const`.
1422+
// Therefore we have to remap the param env here to be non-const.
1423+
hir::def::DefKind::AssocConst => true,
1424+
hir::def::DefKind::AssocFn
1425+
if self.tcx.def_kind(self.tcx.parent(def_id)) == hir::def::DefKind::Trait =>
1426+
{
1427+
// N.B.: All callsites to this function involve checking a path expression.
1428+
//
1429+
// When instantiating a trait method as a function item, it does not actually matter whether
1430+
// the trait is `const` or not, or whether `where T: ~const Tr` needs to be satisfied as
1431+
// `const`. If we were to introduce instantiating trait methods as `const fn`s, we would
1432+
// check that after this, either via a bound `where F: ~const FnOnce` or when coercing to a
1433+
// `const fn` pointer.
1434+
//
1435+
// FIXME(fee1-dead) FIXME(const_trait_impl): update this doc when trait methods can satisfy
1436+
// `~const FnOnce` or can be coerced to `const fn` pointer.
1437+
true
1438+
}
1439+
_ => false,
1440+
};
14171441
let (bounds, _) = self.instantiate_bounds(span, def_id, &substs);
14181442

1419-
for obligation in traits::predicates_for_generics(
1443+
for mut obligation in traits::predicates_for_generics(
14201444
|idx, predicate_span| {
14211445
traits::ObligationCause::new(span, self.body_id, code(idx, predicate_span))
14221446
},
1423-
self.param_env,
1447+
param_env,
14241448
bounds,
14251449
) {
1450+
if remap {
1451+
obligation = obligation.without_const(self.tcx);
1452+
}
14261453
self.register_predicate(obligation);
14271454
}
14281455
}

compiler/rustc_typeck/src/check/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ fn typeck_with_fallback<'tcx>(
366366

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

460460
// Closure and generator analysis may run after fallback
461461
// because they don't constrain other type variables.
462+
// Closure analysis only runs on closures. Therefore they only need to fulfill non-const predicates (as of now)
463+
let prev_constness = fcx.param_env.constness();
464+
fcx.param_env = fcx.param_env.without_const();
462465
fcx.closure_analyze(body);
466+
fcx.param_env = fcx.param_env.with_constness(prev_constness);
463467
assert!(fcx.deferred_call_resolutions.borrow().is_empty());
464468
// Before the generator analysis, temporary scopes shall be marked to provide more
465469
// precise information on types to be captured.

0 commit comments

Comments
 (0)