Skip to content

Commit 86193f1

Browse files
Check const_eval_select intrinsic correctly
1 parent 6e32d44 commit 86193f1

File tree

3 files changed

+69
-30
lines changed

3 files changed

+69
-30
lines changed

compiler/rustc_hir_typeck/src/callee.rs

+63
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,69 @@ 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+
// Check that second and third argument of `const_eval_select` must be `FnDef`, and additionally that
483+
// the second argument must be `const fn`. The first argument must be a tuple, but this is already expressed
484+
// in the function signature (`F: FnOnce<ARG>`), so I did not bother to add another check here.
485+
//
486+
// This check is here because there is currently no way to express a trait bound for `FnDef` types only.
487+
if let ty::FnDef(def_id, _args) = *arg_ty.kind() {
488+
let fn_once_def_id =
489+
self.tcx.require_lang_item(hir::LangItem::FnOnce, Some(call_expr.span));
490+
let fn_once_output_def_id = self
491+
.tcx
492+
.require_lang_item(hir::LangItem::FnOnceOutput, Some(call_expr.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
496+
.sess
497+
.emit_err(errors::ConstSelectMustBeConst { span: call_expr.span });
498+
}
499+
} else {
500+
let const_param: ty::GenericArg<'tcx> =
501+
([self.tcx.consts.false_, self.tcx.consts.true_])[idx].into();
502+
self.register_predicate(traits::Obligation::new(
503+
self.tcx,
504+
self.misc(call_expr.span),
505+
self.param_env,
506+
ty::TraitRef::new(
507+
self.tcx,
508+
fn_once_def_id,
509+
[arg_ty.into(), fn_sig.inputs()[0].into(), const_param],
510+
),
511+
));
512+
513+
self.register_predicate(traits::Obligation::new(
514+
self.tcx,
515+
self.misc(call_expr.span),
516+
self.param_env,
517+
ty::ProjectionPredicate {
518+
projection_ty: ty::AliasTy::new(
519+
self.tcx,
520+
fn_once_output_def_id,
521+
[arg_ty.into(), fn_sig.inputs()[0].into(), const_param],
522+
),
523+
term: fn_sig.output().into(),
524+
},
525+
));
526+
527+
self.select_obligations_where_possible(|_| {});
528+
}
529+
} else {
530+
self.tcx
531+
.sess
532+
.emit_err(errors::ConstSelectMustBeFn { span: call_expr.span, ty: arg_ty });
533+
}
534+
}
535+
}
536+
474537
fn_sig.output()
475538
}
476539

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(

tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -518,5 +518,10 @@ extern "rust-intrinsic" {
518518
arg: ARG,
519519
called_in_const: F,
520520
called_at_rt: G,
521-
) -> RET;
521+
) -> RET
522+
/* where clauses enforced by built-in method confirmation:
523+
where
524+
F: const FnOnce<Arg, Output = RET>,
525+
G: FnOnce<Arg, Output = RET>,
526+
*/;
522527
}

0 commit comments

Comments
 (0)