From faea6ad5792f9d6c0b12e3b8212815ff64169121 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 16 Dec 2023 19:20:00 +0000 Subject: [PATCH] Check const_eval_select intrinsic correctly --- compiler/rustc_hir_typeck/src/callee.rs | 59 +++++++++++++++++++ .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 29 --------- .../intrinsics/const-eval-select-bad.stderr | 36 +++++------ .../effects/minicore.rs | 7 ++- 4 files changed, 83 insertions(+), 48 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 51f38240033..5e6b54950b3 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -471,6 +471,65 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + if let Some(def_id) = def_id + && self.tcx.def_kind(def_id) == hir::def::DefKind::Fn + && self.tcx.is_intrinsic(def_id) + && self.tcx.item_name(def_id) == sym::const_eval_select + { + let fn_sig = self.resolve_vars_if_possible(fn_sig); + for idx in 0..=1 { + let arg_ty = fn_sig.inputs()[idx + 1]; + let span = arg_exprs.get(idx + 1).map_or(call_expr.span, |arg| arg.span); + // Check that second and third argument of `const_eval_select` must be `FnDef`, and additionally that + // the second argument must be `const fn`. The first argument must be a tuple, but this is already expressed + // in the function signature (`F: FnOnce`), so I did not bother to add another check here. + // + // This check is here because there is currently no way to express a trait bound for `FnDef` types only. + if let ty::FnDef(def_id, _args) = *arg_ty.kind() { + let fn_once_def_id = + self.tcx.require_lang_item(hir::LangItem::FnOnce, Some(span)); + let fn_once_output_def_id = + self.tcx.require_lang_item(hir::LangItem::FnOnceOutput, Some(span)); + if self.tcx.generics_of(fn_once_def_id).host_effect_index.is_none() { + if idx == 0 && !self.tcx.is_const_fn_raw(def_id) { + self.tcx.sess.emit_err(errors::ConstSelectMustBeConst { span }); + } + } else { + let const_param: ty::GenericArg<'tcx> = + ([self.tcx.consts.false_, self.tcx.consts.true_])[idx].into(); + self.register_predicate(traits::Obligation::new( + self.tcx, + self.misc(span), + self.param_env, + ty::TraitRef::new( + self.tcx, + fn_once_def_id, + [arg_ty.into(), fn_sig.inputs()[0].into(), const_param], + ), + )); + + self.register_predicate(traits::Obligation::new( + self.tcx, + self.misc(span), + self.param_env, + ty::ProjectionPredicate { + projection_ty: ty::AliasTy::new( + self.tcx, + fn_once_output_def_id, + [arg_ty.into(), fn_sig.inputs()[0].into(), const_param], + ), + term: fn_sig.output().into(), + }, + )); + + self.select_obligations_where_possible(|_| {}); + } + } else { + self.tcx.sess.emit_err(errors::ConstSelectMustBeFn { span, ty: arg_ty }); + } + } + } + fn_sig.output() } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 17022f1fd37..4caa0df58b6 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -230,11 +230,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let minimum_input_count = expected_input_tys.len(); let provided_arg_count = provided_args.len(); - let is_const_eval_select = matches!(fn_def_id, Some(def_id) if - self.tcx.def_kind(def_id) == hir::def::DefKind::Fn - && self.tcx.is_intrinsic(def_id) - && self.tcx.item_name(def_id) == sym::const_eval_select); - // We introduce a helper function to demand that a given argument satisfy a given input // This is more complicated than just checking type equality, as arguments could be coerced // This version writes those types back so further type checking uses the narrowed types @@ -269,30 +264,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Compatibility::Incompatible(coerce_error); } - // Check that second and third argument of `const_eval_select` must be `FnDef`, and additionally that - // the second argument must be `const fn`. The first argument must be a tuple, but this is already expressed - // in the function signature (`F: FnOnce`), so I did not bother to add another check here. - // - // This check is here because there is currently no way to express a trait bound for `FnDef` types only. - if is_const_eval_select && (1..=2).contains(&idx) { - if let ty::FnDef(def_id, args) = *checked_ty.kind() { - if idx == 1 { - if !self.tcx.is_const_fn_raw(def_id) { - self.tcx.sess.emit_err(errors::ConstSelectMustBeConst { - span: provided_arg.span, - }); - } else { - self.enforce_context_effects(provided_arg.span, def_id, args) - } - } - } else { - self.tcx.sess.emit_err(errors::ConstSelectMustBeFn { - span: provided_arg.span, - ty: checked_ty, - }); - } - } - // 3. Check if the formal type is a supertype of the checked one // and register any such obligations for future type checks let supertype_error = self.at(&self.misc(provided_arg.span), self.param_env).sup( diff --git a/tests/ui/intrinsics/const-eval-select-bad.stderr b/tests/ui/intrinsics/const-eval-select-bad.stderr index bf91dc72cc1..85e22178c4a 100644 --- a/tests/ui/intrinsics/const-eval-select-bad.stderr +++ b/tests/ui/intrinsics/const-eval-select-bad.stderr @@ -16,15 +16,6 @@ LL | const_eval_select((), || {}, || {}); = note: expected a function item, found {closure@$DIR/const-eval-select-bad.rs:7:34: 7:36} = help: consult the documentation on `const_eval_select` for more information -error: this argument must be a function item - --> $DIR/const-eval-select-bad.rs:10:27 - | -LL | const_eval_select((), 42, 0xDEADBEEF); - | ^^ - | - = note: expected a function item, found {integer} - = help: consult the documentation on `const_eval_select` for more information - error[E0277]: expected a `FnOnce()` closure, found `{integer}` --> $DIR/const-eval-select-bad.rs:10:27 | @@ -38,15 +29,6 @@ LL | const_eval_select((), 42, 0xDEADBEEF); note: required by a bound in `const_eval_select` --> $SRC_DIR/core/src/intrinsics.rs:LL:COL -error: this argument must be a function item - --> $DIR/const-eval-select-bad.rs:10:31 - | -LL | const_eval_select((), 42, 0xDEADBEEF); - | ^^^^^^^^^^ - | - = note: expected a function item, found {integer} - = help: consult the documentation on `const_eval_select` for more information - error[E0277]: expected a `FnOnce()` closure, found `{integer}` --> $DIR/const-eval-select-bad.rs:10:31 | @@ -60,6 +42,24 @@ LL | const_eval_select((), 42, 0xDEADBEEF); note: required by a bound in `const_eval_select` --> $SRC_DIR/core/src/intrinsics.rs:LL:COL +error: this argument must be a function item + --> $DIR/const-eval-select-bad.rs:10:27 + | +LL | const_eval_select((), 42, 0xDEADBEEF); + | ^^ + | + = note: expected a function item, found {integer} + = help: consult the documentation on `const_eval_select` for more information + +error: this argument must be a function item + --> $DIR/const-eval-select-bad.rs:10:31 + | +LL | const_eval_select((), 42, 0xDEADBEEF); + | ^^^^^^^^^^ + | + = note: expected a function item, found {integer} + = help: consult the documentation on `const_eval_select` for more information + error[E0271]: expected `bar` to be a fn item that returns `i32`, but it returns `bool` --> $DIR/const-eval-select-bad.rs:32:34 | diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs index dc0e00bae4e..59fb48e794c 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs @@ -518,5 +518,10 @@ extern "rust-intrinsic" { arg: ARG, called_in_const: F, called_at_rt: G, - ) -> RET; + ) -> RET + /* where clauses enforced by built-in method confirmation: + where + F: const FnOnce, + G: FnOnce, + */; }