about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2023-02-10 02:10:42 +0000
committerMichael Goulet <michael@errs.io>2023-02-18 19:32:58 +0000
commit82b52056fe99705b4234c5781eada2bd7ae6042e (patch)
treeed9a5baf83c9879c7de33adb6a88cd5990654de9
parent3eb5c4581a386b13c414e8c8bd73846ef37236d1 (diff)
downloadrust-82b52056fe99705b4234c5781eada2bd7ae6042e.tar.gz
rust-82b52056fe99705b4234c5781eada2bd7ae6042e.zip
Check that built-in callable types validate their output type is `Sized` (in new solver)
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly.rs12
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals.rs22
-rw-r--r--compiler/rustc_trait_selection/src/solve/trait_goals.rs25
-rw-r--r--tests/ui/traits/new-solver/builtin-fn-must-return-sized.rs17
-rw-r--r--tests/ui/traits/new-solver/builtin-fn-must-return-sized.stderr18
5 files changed, 80 insertions, 14 deletions
diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs
index 126ec60b3d6..a9e0c5509a5 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly.rs
@@ -96,10 +96,22 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy + Eq {
         impl_def_id: DefId,
     ) -> QueryResult<'tcx>;
 
+    // Consider a predicate we know holds (`assumption`) against a goal we're trying to prove.
     fn consider_assumption(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
         assumption: ty::Predicate<'tcx>,
+    ) -> QueryResult<'tcx> {
+        Self::consider_assumption_with_certainty(ecx, goal, assumption, Certainty::Yes)
+    }
+
+    // Consider a predicate we know holds (`assumption`) against a goal, unifying with
+    // the `assumption_certainty` if it satisfies the goal.
+    fn consider_assumption_with_certainty(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+        assumption: ty::Predicate<'tcx>,
+        assumption_certainty: Certainty,
     ) -> QueryResult<'tcx>;
 
     // A type implements an `auto trait` if its components do as well. These components
diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs
index 883342148f4..adf8a1690c8 100644
--- a/compiler/rustc_trait_selection/src/solve/project_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs
@@ -260,10 +260,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
         })
     }
 
-    fn consider_assumption(
+    fn consider_assumption_with_certainty(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
         assumption: ty::Predicate<'tcx>,
+        assumption_certainty: Certainty,
     ) -> QueryResult<'tcx> {
         if let Some(poly_projection_pred) = assumption.to_opt_poly_projection_pred()
             && poly_projection_pred.projection_def_id() == goal.predicate.def_id()
@@ -280,7 +281,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
 
                 ecx.eq_term_and_make_canonical_response(
                     goal,
-                    subst_certainty,
+                    subst_certainty.unify_and(assumption_certainty),
                     assumption_projection_pred.term,
                 )
             })
@@ -329,22 +330,29 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
         goal: Goal<'tcx, Self>,
         goal_kind: ty::ClosureKind,
     ) -> QueryResult<'tcx> {
+        let tcx = ecx.tcx();
         if let Some(tupled_inputs_and_output) =
             structural_traits::extract_tupled_inputs_and_output_from_callable(
-                ecx.tcx(),
+                tcx,
                 goal.predicate.self_ty(),
                 goal_kind,
             )?
         {
+            // A built-in `Fn` trait needs to check that its output is `Sized`
+            // (FIXME: technically we only need to check this if the type is a fn ptr...)
+            let output_is_sized_pred = tupled_inputs_and_output
+                .map_bound(|(_, output)| tcx.at(DUMMY_SP).mk_trait_ref(LangItem::Sized, [output]));
+            let (_, output_is_sized_certainty) =
+                ecx.evaluate_goal(goal.with(tcx, output_is_sized_pred))?;
+
             let pred = tupled_inputs_and_output
                 .map_bound(|(inputs, output)| ty::ProjectionPredicate {
-                    projection_ty: ecx
-                        .tcx()
+                    projection_ty: tcx
                         .mk_alias_ty(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]),
                     term: output.into(),
                 })
-                .to_predicate(ecx.tcx());
-            Self::consider_assumption(ecx, goal, pred)
+                .to_predicate(tcx);
+            Self::consider_assumption_with_certainty(ecx, goal, pred, output_is_sized_certainty)
         } else {
             ecx.make_canonical_response(Certainty::AMBIGUOUS)
         }
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
index d12e5f797fb..dd47a5ae150 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
@@ -6,6 +6,7 @@ use super::assembly;
 use super::infcx_ext::InferCtxtExt;
 use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, QueryResult};
 use rustc_hir::def_id::DefId;
+use rustc_hir::LangItem;
 use rustc_infer::infer::InferCtxt;
 use rustc_infer::traits::query::NoSolution;
 use rustc_infer::traits::util::supertraits;
@@ -61,10 +62,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         })
     }
 
-    fn consider_assumption(
+    fn consider_assumption_with_certainty(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
         assumption: ty::Predicate<'tcx>,
+        assumption_certainty: Certainty,
     ) -> QueryResult<'tcx> {
         if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred()
             && poly_trait_pred.def_id() == goal.predicate.def_id()
@@ -78,7 +80,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
                     goal.predicate.trait_ref,
                     assumption_trait_pred.trait_ref,
                 )?;
-                ecx.evaluate_all_and_make_canonical_response(nested_goals)
+                ecx.evaluate_all(nested_goals).and_then(|certainty| {
+                    ecx.make_canonical_response(certainty.unify_and(assumption_certainty))
+                })
             })
         } else {
             Err(NoSolution)
@@ -173,20 +177,27 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         goal: Goal<'tcx, Self>,
         goal_kind: ty::ClosureKind,
     ) -> QueryResult<'tcx> {
+        let tcx = ecx.tcx();
         if let Some(tupled_inputs_and_output) =
             structural_traits::extract_tupled_inputs_and_output_from_callable(
-                ecx.tcx(),
+                tcx,
                 goal.predicate.self_ty(),
                 goal_kind,
             )?
         {
+            // A built-in `Fn` trait needs to check that its output is `Sized`
+            // (FIXME: technically we only need to check this if the type is a fn ptr...)
+            let output_is_sized_pred = tupled_inputs_and_output
+                .map_bound(|(_, output)| tcx.at(DUMMY_SP).mk_trait_ref(LangItem::Sized, [output]));
+            let (_, output_is_sized_certainty) =
+                ecx.evaluate_goal(goal.with(tcx, output_is_sized_pred))?;
+
             let pred = tupled_inputs_and_output
                 .map_bound(|(inputs, _)| {
-                    ecx.tcx()
-                        .mk_trait_ref(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
+                    tcx.mk_trait_ref(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
                 })
-                .to_predicate(ecx.tcx());
-            Self::consider_assumption(ecx, goal, pred)
+                .to_predicate(tcx);
+            Self::consider_assumption_with_certainty(ecx, goal, pred, output_is_sized_certainty)
         } else {
             ecx.make_canonical_response(Certainty::AMBIGUOUS)
         }
diff --git a/tests/ui/traits/new-solver/builtin-fn-must-return-sized.rs b/tests/ui/traits/new-solver/builtin-fn-must-return-sized.rs
new file mode 100644
index 00000000000..ba473653ecf
--- /dev/null
+++ b/tests/ui/traits/new-solver/builtin-fn-must-return-sized.rs
@@ -0,0 +1,17 @@
+// compile-flags: -Ztrait-solver=next
+
+#![feature(fn_traits)]
+#![feature(unboxed_closures)]
+#![feature(tuple_trait)]
+
+use std::ops::Fn;
+use std::marker::Tuple;
+
+fn foo<F: Fn<T>, T: Tuple>(f: Option<F>, t: T) {
+    let y = (f.unwrap()).call(t);
+}
+
+fn main() {
+    foo::<fn() -> str, _>(None, ());
+    //~^ expected a `Fn<_>` closure, found `fn() -> str`
+}
diff --git a/tests/ui/traits/new-solver/builtin-fn-must-return-sized.stderr b/tests/ui/traits/new-solver/builtin-fn-must-return-sized.stderr
new file mode 100644
index 00000000000..f7551739b13
--- /dev/null
+++ b/tests/ui/traits/new-solver/builtin-fn-must-return-sized.stderr
@@ -0,0 +1,18 @@
+error[E0277]: expected a `Fn<_>` closure, found `fn() -> str`
+  --> $DIR/builtin-fn-must-return-sized.rs:15:27
+   |
+LL |     foo::<fn() -> str, _>(None, ());
+   |     --------------------- ^^^^ expected an `Fn<_>` closure, found `fn() -> str`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Fn<_>` is not implemented for `fn() -> str`
+note: required by a bound in `foo`
+  --> $DIR/builtin-fn-must-return-sized.rs:10:11
+   |
+LL | fn foo<F: Fn<T>, T: Tuple>(f: Option<F>, t: T) {
+   |           ^^^^^ required by this bound in `foo`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.