Skip to content

Commit 3f28fe1

Browse files
committed
Auto merge of #119023 - compiler-errors:fn-trait-constness, r=fee1-dead
Check `FnPtr`/`FnDef` built-in fn traits correctly with effects 1. Teach the (old) trait solver how to handle the constness for built-in impls of the `Fn*` family of traits. This unfortunately doesn't support const closures just yet. 2. Fix the `const_eval_select`. It turns out that the `where` clause bounds on `const_eval_select` force the effect parameter for both fndefs to be `true` -- with effects, we will leave off any explicit where clauses and register these obligations manually. I can elaborate on (2.) if you think it needs a better explanation! r? fee1-dead
2 parents bf9229a + faea6ad commit 3f28fe1

File tree

10 files changed

+164
-69
lines changed

10 files changed

+164
-69
lines changed

compiler/rustc_hir_typeck/src/callee.rs

+59
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,65 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
471471
}
472472
}
473473

474+
if let Some(def_id) = def_id
475+
&& self.tcx.def_kind(def_id) == hir::def::DefKind::Fn
476+
&& self.tcx.is_intrinsic(def_id)
477+
&& self.tcx.item_name(def_id) == sym::const_eval_select
478+
{
479+
let fn_sig = self.resolve_vars_if_possible(fn_sig);
480+
for idx in 0..=1 {
481+
let arg_ty = fn_sig.inputs()[idx + 1];
482+
let span = arg_exprs.get(idx + 1).map_or(call_expr.span, |arg| arg.span);
483+
// Check that second and third argument of `const_eval_select` must be `FnDef`, and additionally that
484+
// the second argument must be `const fn`. The first argument must be a tuple, but this is already expressed
485+
// in the function signature (`F: FnOnce<ARG>`), so I did not bother to add another check here.
486+
//
487+
// This check is here because there is currently no way to express a trait bound for `FnDef` types only.
488+
if let ty::FnDef(def_id, _args) = *arg_ty.kind() {
489+
let fn_once_def_id =
490+
self.tcx.require_lang_item(hir::LangItem::FnOnce, Some(span));
491+
let fn_once_output_def_id =
492+
self.tcx.require_lang_item(hir::LangItem::FnOnceOutput, Some(span));
493+
if self.tcx.generics_of(fn_once_def_id).host_effect_index.is_none() {
494+
if idx == 0 && !self.tcx.is_const_fn_raw(def_id) {
495+
self.tcx.sess.emit_err(errors::ConstSelectMustBeConst { span });
496+
}
497+
} else {
498+
let const_param: ty::GenericArg<'tcx> =
499+
([self.tcx.consts.false_, self.tcx.consts.true_])[idx].into();
500+
self.register_predicate(traits::Obligation::new(
501+
self.tcx,
502+
self.misc(span),
503+
self.param_env,
504+
ty::TraitRef::new(
505+
self.tcx,
506+
fn_once_def_id,
507+
[arg_ty.into(), fn_sig.inputs()[0].into(), const_param],
508+
),
509+
));
510+
511+
self.register_predicate(traits::Obligation::new(
512+
self.tcx,
513+
self.misc(span),
514+
self.param_env,
515+
ty::ProjectionPredicate {
516+
projection_ty: ty::AliasTy::new(
517+
self.tcx,
518+
fn_once_output_def_id,
519+
[arg_ty.into(), fn_sig.inputs()[0].into(), const_param],
520+
),
521+
term: fn_sig.output().into(),
522+
},
523+
));
524+
525+
self.select_obligations_where_possible(|_| {});
526+
}
527+
} else {
528+
self.tcx.sess.emit_err(errors::ConstSelectMustBeFn { span, ty: arg_ty });
529+
}
530+
}
531+
}
532+
474533
fn_sig.output()
475534
}
476535

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

-29
Original file line numberDiff line numberDiff line change
@@ -230,11 +230,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
230230
let minimum_input_count = expected_input_tys.len();
231231
let provided_arg_count = provided_args.len();
232232

233-
let is_const_eval_select = matches!(fn_def_id, Some(def_id) if
234-
self.tcx.def_kind(def_id) == hir::def::DefKind::Fn
235-
&& self.tcx.is_intrinsic(def_id)
236-
&& self.tcx.item_name(def_id) == sym::const_eval_select);
237-
238233
// We introduce a helper function to demand that a given argument satisfy a given input
239234
// This is more complicated than just checking type equality, as arguments could be coerced
240235
// This version writes those types back so further type checking uses the narrowed types
@@ -269,30 +264,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
269264
return Compatibility::Incompatible(coerce_error);
270265
}
271266

272-
// Check that second and third argument of `const_eval_select` must be `FnDef`, and additionally that
273-
// the second argument must be `const fn`. The first argument must be a tuple, but this is already expressed
274-
// in the function signature (`F: FnOnce<ARG>`), so I did not bother to add another check here.
275-
//
276-
// This check is here because there is currently no way to express a trait bound for `FnDef` types only.
277-
if is_const_eval_select && (1..=2).contains(&idx) {
278-
if let ty::FnDef(def_id, args) = *checked_ty.kind() {
279-
if idx == 1 {
280-
if !self.tcx.is_const_fn_raw(def_id) {
281-
self.tcx.sess.emit_err(errors::ConstSelectMustBeConst {
282-
span: provided_arg.span,
283-
});
284-
} else {
285-
self.enforce_context_effects(provided_arg.span, def_id, args)
286-
}
287-
}
288-
} else {
289-
self.tcx.sess.emit_err(errors::ConstSelectMustBeFn {
290-
span: provided_arg.span,
291-
ty: checked_ty,
292-
});
293-
}
294-
}
295-
296267
// 3. Check if the formal type is a supertype of the checked one
297268
// and register any such obligations for future type checks
298269
let supertype_error = self.at(&self.misc(provided_arg.span), self.param_env).sup(

compiler/rustc_middle/src/traits/select.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ pub enum SelectionCandidate<'tcx> {
153153
/// Implementation of a `Fn`-family trait by one of the anonymous
154154
/// types generated for a fn pointer type (e.g., `fn(int) -> int`)
155155
FnPointerCandidate {
156-
is_const: bool,
156+
fn_host_effect: ty::Const<'tcx>,
157157
},
158158

159159
TraitAliasCandidate,

compiler/rustc_trait_selection/src/traits/project.rs

+32-7
Original file line numberDiff line numberDiff line change
@@ -2327,8 +2327,9 @@ fn confirm_fn_pointer_candidate<'cx, 'tcx>(
23272327
obligation: &ProjectionTyObligation<'tcx>,
23282328
nested: Vec<PredicateObligation<'tcx>>,
23292329
) -> Progress<'tcx> {
2330+
let tcx = selcx.tcx();
23302331
let fn_type = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
2331-
let sig = fn_type.fn_sig(selcx.tcx());
2332+
let sig = fn_type.fn_sig(tcx);
23322333
let Normalized { value: sig, obligations } = normalize_with_depth(
23332334
selcx,
23342335
obligation.param_env,
@@ -2337,9 +2338,24 @@ fn confirm_fn_pointer_candidate<'cx, 'tcx>(
23372338
sig,
23382339
);
23392340

2340-
confirm_callable_candidate(selcx, obligation, sig, util::TupleArgumentsFlag::Yes)
2341-
.with_addl_obligations(nested)
2342-
.with_addl_obligations(obligations)
2341+
let host_effect_param = match *fn_type.kind() {
2342+
ty::FnDef(def_id, args) => tcx
2343+
.generics_of(def_id)
2344+
.host_effect_index
2345+
.map_or(tcx.consts.true_, |idx| args.const_at(idx)),
2346+
ty::FnPtr(_) => tcx.consts.true_,
2347+
_ => unreachable!("only expected FnPtr or FnDef in `confirm_fn_pointer_candidate`"),
2348+
};
2349+
2350+
confirm_callable_candidate(
2351+
selcx,
2352+
obligation,
2353+
sig,
2354+
util::TupleArgumentsFlag::Yes,
2355+
host_effect_param,
2356+
)
2357+
.with_addl_obligations(nested)
2358+
.with_addl_obligations(obligations)
23432359
}
23442360

23452361
fn confirm_closure_candidate<'cx, 'tcx>(
@@ -2362,16 +2378,24 @@ fn confirm_closure_candidate<'cx, 'tcx>(
23622378

23632379
debug!(?obligation, ?closure_sig, ?obligations, "confirm_closure_candidate");
23642380

2365-
confirm_callable_candidate(selcx, obligation, closure_sig, util::TupleArgumentsFlag::No)
2366-
.with_addl_obligations(nested)
2367-
.with_addl_obligations(obligations)
2381+
confirm_callable_candidate(
2382+
selcx,
2383+
obligation,
2384+
closure_sig,
2385+
util::TupleArgumentsFlag::No,
2386+
// FIXME(effects): This doesn't handle const closures correctly!
2387+
selcx.tcx().consts.true_,
2388+
)
2389+
.with_addl_obligations(nested)
2390+
.with_addl_obligations(obligations)
23682391
}
23692392

23702393
fn confirm_callable_candidate<'cx, 'tcx>(
23712394
selcx: &mut SelectionContext<'cx, 'tcx>,
23722395
obligation: &ProjectionTyObligation<'tcx>,
23732396
fn_sig: ty::PolyFnSig<'tcx>,
23742397
flag: util::TupleArgumentsFlag,
2398+
fn_host_effect: ty::Const<'tcx>,
23752399
) -> Progress<'tcx> {
23762400
let tcx = selcx.tcx();
23772401

@@ -2386,6 +2410,7 @@ fn confirm_callable_candidate<'cx, 'tcx>(
23862410
obligation.predicate.self_ty(),
23872411
fn_sig,
23882412
flag,
2413+
fn_host_effect,
23892414
)
23902415
.map_bound(|(trait_ref, ret_type)| ty::ProjectionPredicate {
23912416
projection_ty: ty::AliasTy::new(tcx, fn_once_output_def_id, trait_ref.args),

compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

+13-7
Original file line numberDiff line numberDiff line change
@@ -355,17 +355,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
355355
// Provide an impl, but only for suitable `fn` pointers.
356356
ty::FnPtr(sig) => {
357357
if sig.is_fn_trait_compatible() {
358-
candidates.vec.push(FnPointerCandidate { is_const: false });
358+
candidates
359+
.vec
360+
.push(FnPointerCandidate { fn_host_effect: self.tcx().consts.true_ });
359361
}
360362
}
361363
// Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396).
362-
ty::FnDef(def_id, _) => {
363-
if self.tcx().fn_sig(def_id).skip_binder().is_fn_trait_compatible()
364-
&& self.tcx().codegen_fn_attrs(def_id).target_features.is_empty()
364+
ty::FnDef(def_id, args) => {
365+
let tcx = self.tcx();
366+
if tcx.fn_sig(def_id).skip_binder().is_fn_trait_compatible()
367+
&& tcx.codegen_fn_attrs(def_id).target_features.is_empty()
365368
{
366-
candidates
367-
.vec
368-
.push(FnPointerCandidate { is_const: self.tcx().is_const_fn(def_id) });
369+
candidates.vec.push(FnPointerCandidate {
370+
fn_host_effect: tcx
371+
.generics_of(def_id)
372+
.host_effect_index
373+
.map_or(tcx.consts.true_, |idx| args.const_at(idx)),
374+
});
369375
}
370376
}
371377
_ => {}

compiler/rustc_trait_selection/src/traits/select/confirmation.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
103103
ImplSource::Builtin(BuiltinImplSource::Misc, vtable_iterator)
104104
}
105105

106-
FnPointerCandidate { is_const } => {
107-
let data = self.confirm_fn_pointer_candidate(obligation, is_const)?;
106+
FnPointerCandidate { fn_host_effect } => {
107+
let data = self.confirm_fn_pointer_candidate(obligation, fn_host_effect)?;
108108
ImplSource::Builtin(BuiltinImplSource::Misc, data)
109109
}
110110

@@ -653,8 +653,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
653653
fn confirm_fn_pointer_candidate(
654654
&mut self,
655655
obligation: &PolyTraitObligation<'tcx>,
656-
// FIXME(effects)
657-
_is_const: bool,
656+
fn_host_effect: ty::Const<'tcx>,
658657
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
659658
debug!(?obligation, "confirm_fn_pointer_candidate");
660659

@@ -675,6 +674,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
675674
self_ty,
676675
sig,
677676
util::TupleArgumentsFlag::Yes,
677+
fn_host_effect,
678678
)
679679
.map_bound(|(trait_ref, _)| trait_ref);
680680

@@ -860,7 +860,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
860860
bug!("closure candidate for non-closure {:?}", obligation);
861861
};
862862

863-
let trait_ref = self.closure_trait_ref_unnormalized(obligation, args);
863+
let trait_ref =
864+
self.closure_trait_ref_unnormalized(obligation, args, self.tcx().consts.true_);
864865
let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
865866

866867
debug!(?closure_def_id, ?trait_ref, ?nested, "confirm closure candidate obligations");

compiler/rustc_trait_selection/src/traits/select/mod.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -1865,7 +1865,9 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
18651865
}
18661866

18671867
// Drop otherwise equivalent non-const fn pointer candidates
1868-
(FnPointerCandidate { .. }, FnPointerCandidate { is_const: false }) => DropVictim::Yes,
1868+
(FnPointerCandidate { .. }, FnPointerCandidate { fn_host_effect }) => {
1869+
DropVictim::drop_if(*fn_host_effect == self.tcx().consts.true_)
1870+
}
18691871

18701872
(
18711873
ParamCandidate(ref other_cand),
@@ -2660,6 +2662,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
26602662
&mut self,
26612663
obligation: &PolyTraitObligation<'tcx>,
26622664
args: GenericArgsRef<'tcx>,
2665+
fn_host_effect: ty::Const<'tcx>,
26632666
) -> ty::PolyTraitRef<'tcx> {
26642667
let closure_sig = args.as_closure().sig();
26652668

@@ -2680,6 +2683,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
26802683
self_ty,
26812684
closure_sig,
26822685
util::TupleArgumentsFlag::No,
2686+
fn_host_effect,
26832687
)
26842688
.map_bound(|(trait_ref, _)| trait_ref)
26852689
}

compiler/rustc_trait_selection/src/traits/util.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -264,13 +264,26 @@ pub fn closure_trait_ref_and_return_type<'tcx>(
264264
self_ty: Ty<'tcx>,
265265
sig: ty::PolyFnSig<'tcx>,
266266
tuple_arguments: TupleArgumentsFlag,
267+
fn_host_effect: ty::Const<'tcx>,
267268
) -> ty::Binder<'tcx, (ty::TraitRef<'tcx>, Ty<'tcx>)> {
268269
assert!(!self_ty.has_escaping_bound_vars());
269270
let arguments_tuple = match tuple_arguments {
270271
TupleArgumentsFlag::No => sig.skip_binder().inputs()[0],
271272
TupleArgumentsFlag::Yes => Ty::new_tup(tcx, sig.skip_binder().inputs()),
272273
};
273-
let trait_ref = ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, arguments_tuple]);
274+
let trait_ref = if tcx.generics_of(fn_trait_def_id).host_effect_index.is_some() {
275+
ty::TraitRef::new(
276+
tcx,
277+
fn_trait_def_id,
278+
[
279+
ty::GenericArg::from(self_ty),
280+
ty::GenericArg::from(arguments_tuple),
281+
ty::GenericArg::from(fn_host_effect),
282+
],
283+
)
284+
} else {
285+
ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, arguments_tuple])
286+
};
274287
sig.map_bound(|sig| (trait_ref, sig.output()))
275288
}
276289

tests/ui/intrinsics/const-eval-select-bad.stderr

+16-16
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,24 @@ LL | const_eval_select((), || {}, || {});
1616
= note: expected a function item, found {closure@$DIR/const-eval-select-bad.rs:7:34: 7:36}
1717
= help: consult the documentation on `const_eval_select` for more information
1818

19-
error: this argument must be a function item
19+
error[E0277]: expected a `FnOnce()` closure, found `{integer}`
2020
--> $DIR/const-eval-select-bad.rs:10:27
2121
|
2222
LL | const_eval_select((), 42, 0xDEADBEEF);
23-
| ^^
23+
| ----------------- ^^ expected an `FnOnce()` closure, found `{integer}`
24+
| |
25+
| required by a bound introduced by this call
2426
|
25-
= note: expected a function item, found {integer}
26-
= help: consult the documentation on `const_eval_select` for more information
27+
= help: the trait `FnOnce<()>` is not implemented for `{integer}`
28+
= note: wrap the `{integer}` in a closure with no arguments: `|| { /* code */ }`
29+
note: required by a bound in `const_eval_select`
30+
--> $SRC_DIR/core/src/intrinsics.rs:LL:COL
2731

2832
error[E0277]: expected a `FnOnce()` closure, found `{integer}`
29-
--> $DIR/const-eval-select-bad.rs:10:27
33+
--> $DIR/const-eval-select-bad.rs:10:31
3034
|
3135
LL | const_eval_select((), 42, 0xDEADBEEF);
32-
| ----------------- ^^ expected an `FnOnce()` closure, found `{integer}`
36+
| ----------------- ^^^^^^^^^^ expected an `FnOnce()` closure, found `{integer}`
3337
| |
3438
| required by a bound introduced by this call
3539
|
@@ -39,26 +43,22 @@ note: required by a bound in `const_eval_select`
3943
--> $SRC_DIR/core/src/intrinsics.rs:LL:COL
4044

4145
error: this argument must be a function item
42-
--> $DIR/const-eval-select-bad.rs:10:31
46+
--> $DIR/const-eval-select-bad.rs:10:27
4347
|
4448
LL | const_eval_select((), 42, 0xDEADBEEF);
45-
| ^^^^^^^^^^
49+
| ^^
4650
|
4751
= note: expected a function item, found {integer}
4852
= help: consult the documentation on `const_eval_select` for more information
4953

50-
error[E0277]: expected a `FnOnce()` closure, found `{integer}`
54+
error: this argument must be a function item
5155
--> $DIR/const-eval-select-bad.rs:10:31
5256
|
5357
LL | const_eval_select((), 42, 0xDEADBEEF);
54-
| ----------------- ^^^^^^^^^^ expected an `FnOnce()` closure, found `{integer}`
55-
| |
56-
| required by a bound introduced by this call
58+
| ^^^^^^^^^^
5759
|
58-
= help: the trait `FnOnce<()>` is not implemented for `{integer}`
59-
= note: wrap the `{integer}` in a closure with no arguments: `|| { /* code */ }`
60-
note: required by a bound in `const_eval_select`
61-
--> $SRC_DIR/core/src/intrinsics.rs:LL:COL
60+
= note: expected a function item, found {integer}
61+
= help: consult the documentation on `const_eval_select` for more information
6262

6363
error[E0271]: expected `bar` to be a fn item that returns `i32`, but it returns `bool`
6464
--> $DIR/const-eval-select-bad.rs:32:34

0 commit comments

Comments
 (0)