about summary refs log tree commit diff
diff options
context:
space:
mode:
authorThePuzzlemaker <tpzker@thepuzzlemaker.info>2021-09-06 16:11:28 -0500
committerThePuzzlemaker <tpzker@thepuzzlemaker.info>2021-09-06 16:30:44 -0500
commit734209ed73507e214e49ef5cb7988af8901e74c7 (patch)
treec5d18c15ab264f37b15b6b09db90ed0129e7a930
parent8ceea01bb442b9746a51b062ce25abbf46d866b2 (diff)
downloadrust-734209ed73507e214e49ef5cb7988af8901e74c7.tar.gz
rust-734209ed73507e214e49ef5cb7988af8901e74c7.zip
Normalize assoc types when checking ret ty of main
This fixes #88609.

Previously, the return type of `fn main()` would not have any associated
type projections within normalized before checking if it implements the
standard library trait `std::process::Termination`. This commit appears
to fix it.

This feels vaguely symptomatic of a problem in the underlying trait
solving engine, but I am not sure how I would solve that. I am unsure
why the example in #88609 with `assert_impl_termination` and `fn foo()`
work, but simply `fn main()` doesn't. The way that I solved this is also
probably not the best way to do this, so please let me know if there is
a better way to do this.

I have added a build-pass regression test for this issue.
-rw-r--r--compiler/rustc_typeck/src/lib.rs18
-rw-r--r--src/test/ui/typeck/issue-88609.rs19
2 files changed, 36 insertions, 1 deletions
diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs
index 749f681e92e..e5d5ca3362b 100644
--- a/compiler/rustc_typeck/src/lib.rs
+++ b/compiler/rustc_typeck/src/lib.rs
@@ -109,6 +109,7 @@ use rustc_middle::util;
 use rustc_session::config::EntryFnType;
 use rustc_span::{symbol::sym, Span, DUMMY_SP};
 use rustc_target::spec::abi::Abi;
+use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
 use rustc_trait_selection::traits::{
     self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt as _,
@@ -328,7 +329,22 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
                 ObligationCauseCode::MainFunctionType,
             );
             let mut fulfillment_cx = traits::FulfillmentContext::new();
-            fulfillment_cx.register_bound(&infcx, ty::ParamEnv::empty(), return_ty, term_id, cause);
+            // normalize any potential projections in the return type, then add
+            // any possible obligations to the fulfillment context.
+            // HACK(ThePuzzlemaker) this feels symptomatic of a problem within
+            // checking trait fulfillment, not this here. I'm not sure why it
+            // works in the example in `fn test()` given in #88609? This also
+            // probably isn't the best way to do this.
+            let normalized = infcx.partially_normalize_associated_types_in(
+                cause.clone(),
+                ty::ParamEnv::empty(),
+                return_ty,
+            );
+            let new_ty = normalized.value;
+            for obligation in normalized.obligations {
+                fulfillment_cx.register_predicate_obligation(&infcx, obligation);
+            }
+            fulfillment_cx.register_bound(&infcx, ty::ParamEnv::empty(), new_ty, term_id, cause);
             if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
                 infcx.report_fulfillment_errors(&err, None, false);
                 error = true;
diff --git a/src/test/ui/typeck/issue-88609.rs b/src/test/ui/typeck/issue-88609.rs
new file mode 100644
index 00000000000..dc459c885fa
--- /dev/null
+++ b/src/test/ui/typeck/issue-88609.rs
@@ -0,0 +1,19 @@
+// Regression test for #88609:
+// The return type for `main` is not normalized while checking if it implements
+// the trait `std::process::Termination`.
+
+// build-pass
+
+trait Same {
+    type Output;
+}
+
+impl<T> Same for T {
+    type Output = T;
+}
+
+type Unit = <() as Same>::Output;
+
+fn main() -> Result<Unit, std::io::Error> {
+    unimplemented!()
+}