From 69f360d00ca24978ad81b52c5815b5a6a6e2a8a7 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 16 Dec 2023 19:21:43 +0000 Subject: [PATCH 1/2] Check FnPtr/FnDef built-in fn traits correctly with effects --- compiler/rustc_middle/src/traits/select.rs | 2 +- .../src/traits/project.rs | 39 +++++++++++++++---- .../src/traits/select/candidate_assembly.rs | 20 ++++++---- .../src/traits/select/confirmation.rs | 11 +++--- .../src/traits/select/mod.rs | 6 ++- .../rustc_trait_selection/src/traits/util.rs | 15 ++++++- .../effects/minicore.rs | 15 ++++++- 7 files changed, 84 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index c52103eb247..734c2b61c07 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -153,7 +153,7 @@ pub enum SelectionCandidate<'tcx> { /// Implementation of a `Fn`-family trait by one of the anonymous /// types generated for a fn pointer type (e.g., `fn(int) -> int`) FnPointerCandidate { - is_const: bool, + fn_host_effect: ty::Const<'tcx>, }, TraitAliasCandidate, diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index d5635812c74..a1b0ada0e8a 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -2327,8 +2327,9 @@ fn confirm_fn_pointer_candidate<'cx, 'tcx>( obligation: &ProjectionTyObligation<'tcx>, nested: Vec>, ) -> Progress<'tcx> { + let tcx = selcx.tcx(); let fn_type = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); - let sig = fn_type.fn_sig(selcx.tcx()); + let sig = fn_type.fn_sig(tcx); let Normalized { value: sig, obligations } = normalize_with_depth( selcx, obligation.param_env, @@ -2337,9 +2338,24 @@ fn confirm_fn_pointer_candidate<'cx, 'tcx>( sig, ); - confirm_callable_candidate(selcx, obligation, sig, util::TupleArgumentsFlag::Yes) - .with_addl_obligations(nested) - .with_addl_obligations(obligations) + let host_effect_param = match *fn_type.kind() { + ty::FnDef(def_id, args) => tcx + .generics_of(def_id) + .host_effect_index + .map_or(tcx.consts.true_, |idx| args.const_at(idx)), + ty::FnPtr(_) => tcx.consts.true_, + _ => unreachable!("only expected FnPtr or FnDef in `confirm_fn_pointer_candidate`"), + }; + + confirm_callable_candidate( + selcx, + obligation, + sig, + util::TupleArgumentsFlag::Yes, + host_effect_param, + ) + .with_addl_obligations(nested) + .with_addl_obligations(obligations) } fn confirm_closure_candidate<'cx, 'tcx>( @@ -2362,9 +2378,16 @@ fn confirm_closure_candidate<'cx, 'tcx>( debug!(?obligation, ?closure_sig, ?obligations, "confirm_closure_candidate"); - confirm_callable_candidate(selcx, obligation, closure_sig, util::TupleArgumentsFlag::No) - .with_addl_obligations(nested) - .with_addl_obligations(obligations) + confirm_callable_candidate( + selcx, + obligation, + closure_sig, + util::TupleArgumentsFlag::No, + // FIXME(effects): This doesn't handle const closures correctly! + selcx.tcx().consts.true_, + ) + .with_addl_obligations(nested) + .with_addl_obligations(obligations) } fn confirm_callable_candidate<'cx, 'tcx>( @@ -2372,6 +2395,7 @@ fn confirm_callable_candidate<'cx, 'tcx>( obligation: &ProjectionTyObligation<'tcx>, fn_sig: ty::PolyFnSig<'tcx>, flag: util::TupleArgumentsFlag, + fn_host_effect: ty::Const<'tcx>, ) -> Progress<'tcx> { let tcx = selcx.tcx(); @@ -2386,6 +2410,7 @@ fn confirm_callable_candidate<'cx, 'tcx>( obligation.predicate.self_ty(), fn_sig, flag, + fn_host_effect, ) .map_bound(|(trait_ref, ret_type)| ty::ProjectionPredicate { projection_ty: ty::AliasTy::new(tcx, fn_once_output_def_id, trait_ref.args), diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index cd7fd028a46..73e06b84085 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -355,17 +355,23 @@ fn assemble_fn_pointer_candidates( // Provide an impl, but only for suitable `fn` pointers. ty::FnPtr(sig) => { if sig.is_fn_trait_compatible() { - candidates.vec.push(FnPointerCandidate { is_const: false }); + candidates + .vec + .push(FnPointerCandidate { fn_host_effect: self.tcx().consts.true_ }); } } // Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396). - ty::FnDef(def_id, _) => { - if self.tcx().fn_sig(def_id).skip_binder().is_fn_trait_compatible() - && self.tcx().codegen_fn_attrs(def_id).target_features.is_empty() + ty::FnDef(def_id, args) => { + let tcx = self.tcx(); + if tcx.fn_sig(def_id).skip_binder().is_fn_trait_compatible() + && tcx.codegen_fn_attrs(def_id).target_features.is_empty() { - candidates - .vec - .push(FnPointerCandidate { is_const: self.tcx().is_const_fn(def_id) }); + candidates.vec.push(FnPointerCandidate { + fn_host_effect: tcx + .generics_of(def_id) + .host_effect_index + .map_or(tcx.consts.true_, |idx| args.const_at(idx)), + }); } } _ => {} diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index bcaf6aff66b..ce3fc2185ba 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -103,8 +103,8 @@ pub(super) fn confirm_candidate( ImplSource::Builtin(BuiltinImplSource::Misc, vtable_iterator) } - FnPointerCandidate { is_const } => { - let data = self.confirm_fn_pointer_candidate(obligation, is_const)?; + FnPointerCandidate { fn_host_effect } => { + let data = self.confirm_fn_pointer_candidate(obligation, fn_host_effect)?; ImplSource::Builtin(BuiltinImplSource::Misc, data) } @@ -653,8 +653,7 @@ fn confirm_object_candidate( fn confirm_fn_pointer_candidate( &mut self, obligation: &PolyTraitObligation<'tcx>, - // FIXME(effects) - _is_const: bool, + fn_host_effect: ty::Const<'tcx>, ) -> Result>, SelectionError<'tcx>> { debug!(?obligation, "confirm_fn_pointer_candidate"); @@ -675,6 +674,7 @@ fn confirm_fn_pointer_candidate( self_ty, sig, util::TupleArgumentsFlag::Yes, + fn_host_effect, ) .map_bound(|(trait_ref, _)| trait_ref); @@ -860,7 +860,8 @@ fn confirm_closure_candidate( bug!("closure candidate for non-closure {:?}", obligation); }; - let trait_ref = self.closure_trait_ref_unnormalized(obligation, args); + let trait_ref = + self.closure_trait_ref_unnormalized(obligation, args, self.tcx().consts.true_); let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?; debug!(?closure_def_id, ?trait_ref, ?nested, "confirm closure candidate obligations"); diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 9886e33ca3b..c7a30535caa 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1865,7 +1865,9 @@ fn candidate_should_be_dropped_in_favor_of( } // Drop otherwise equivalent non-const fn pointer candidates - (FnPointerCandidate { .. }, FnPointerCandidate { is_const: false }) => DropVictim::Yes, + (FnPointerCandidate { .. }, FnPointerCandidate { fn_host_effect }) => { + DropVictim::drop_if(*fn_host_effect == self.tcx().consts.true_) + } ( ParamCandidate(ref other_cand), @@ -2660,6 +2662,7 @@ fn closure_trait_ref_unnormalized( &mut self, obligation: &PolyTraitObligation<'tcx>, args: GenericArgsRef<'tcx>, + fn_host_effect: ty::Const<'tcx>, ) -> ty::PolyTraitRef<'tcx> { let closure_sig = args.as_closure().sig(); @@ -2680,6 +2683,7 @@ fn closure_trait_ref_unnormalized( self_ty, closure_sig, util::TupleArgumentsFlag::No, + fn_host_effect, ) .map_bound(|(trait_ref, _)| trait_ref) } diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 575010ff46d..19eae93df9c 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -264,13 +264,26 @@ pub fn closure_trait_ref_and_return_type<'tcx>( self_ty: Ty<'tcx>, sig: ty::PolyFnSig<'tcx>, tuple_arguments: TupleArgumentsFlag, + fn_host_effect: ty::Const<'tcx>, ) -> ty::Binder<'tcx, (ty::TraitRef<'tcx>, Ty<'tcx>)> { assert!(!self_ty.has_escaping_bound_vars()); let arguments_tuple = match tuple_arguments { TupleArgumentsFlag::No => sig.skip_binder().inputs()[0], TupleArgumentsFlag::Yes => Ty::new_tup(tcx, sig.skip_binder().inputs()), }; - let trait_ref = ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, arguments_tuple]); + let trait_ref = if tcx.generics_of(fn_trait_def_id).host_effect_index.is_some() { + ty::TraitRef::new( + tcx, + fn_trait_def_id, + [ + ty::GenericArg::from(self_ty), + ty::GenericArg::from(arguments_tuple), + ty::GenericArg::from(fn_host_effect), + ], + ) + } else { + ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, arguments_tuple]) + }; sig.map_bound(|sig| (trait_ref, sig.output())) } 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 2a2e8cec3f0..dc0e00bae4e 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 @@ -1,12 +1,13 @@ // check-pass #![crate_type = "lib"] -#![feature(no_core, lang_items, unboxed_closures, auto_traits, intrinsics, rustc_attrs)] +#![feature(no_core, lang_items, unboxed_closures, auto_traits, intrinsics, rustc_attrs, staged_api)] #![feature(fundamental)] #![feature(const_trait_impl, effects, const_mut_refs)] #![allow(internal_features)] #![no_std] #![no_core] +#![stable(feature = "minicore", since = "1.0.0")] #[lang = "sized"] trait Sized {} @@ -82,6 +83,7 @@ trait FnMut: ~const FnOnce { #[lang = "fn_once"] #[rustc_paren_sugar] trait FnOnce { + #[lang = "fn_once_output"] type Output; extern "rust-call" fn call_once(self, args: Args) -> Self::Output; @@ -93,7 +95,7 @@ struct ConstFnMutClosure { } #[lang = "tuple_trait"] -pub trait Tuple {} +trait Tuple {} macro_rules! impl_fn_mut_tuple { ($($var:ident)*) => { @@ -509,3 +511,12 @@ trait StructuralPartialEq {} trait StructuralEq {} const fn drop(_: T) {} + +extern "rust-intrinsic" { + #[rustc_const_stable(feature = "const_eval_select", since = "1.0.0")] + fn const_eval_select( + arg: ARG, + called_in_const: F, + called_at_rt: G, + ) -> RET; +} From faea6ad5792f9d6c0b12e3b8212815ff64169121 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 16 Dec 2023 19:20:00 +0000 Subject: [PATCH 2/2] 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 @@ fn confirm_builtin_call( } } + 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 @@ pub(in super::super) fn check_argument_types( 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 @@ pub(in super::super) fn check_argument_types( 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 @@ fn const_eval_select( 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, + */; }