From 3dbc9ed24fe32679816359f9f94c2df15007298f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 9 Oct 2022 19:04:22 +0000 Subject: [PATCH 1/3] Properly bubble up ambiguity in normalize query --- compiler/rustc_middle/src/traits/query.rs | 27 +++++++++++++++++++ .../src/traits/query/normalize.rs | 14 +++++----- compiler/rustc_traits/src/dropck_outlives.rs | 4 +-- .../src/normalize_erasing_regions.rs | 2 +- compiler/rustc_traits/src/type_op.rs | 6 +++-- src/test/rustdoc/issue-102835.rs | 21 +++++++++++++++ 6 files changed, 62 insertions(+), 12 deletions(-) create mode 100644 src/test/rustdoc/issue-102835.rs diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index fb152b63f6344..a943161efd1ef 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -109,6 +109,33 @@ impl<'tcx> From> for NoSolution { } } +#[derive(Copy, Clone, Debug, HashStable)] +pub enum NoSolutionOrAmbiguous { + NoSolution, + Ambiguous, +} + +impl NoSolutionOrAmbiguous { + pub fn expect_unambiguous(self) -> NoSolution { + match self { + NoSolutionOrAmbiguous::NoSolution => NoSolution, + NoSolutionOrAmbiguous::Ambiguous => bug!("unexpected ambiguity"), + } + } +} + +impl From for NoSolutionOrAmbiguous { + fn from(_: NoSolution) -> NoSolutionOrAmbiguous { + NoSolutionOrAmbiguous::NoSolution + } +} + +impl<'tcx> From> for NoSolutionOrAmbiguous { + fn from(_: TypeError<'tcx>) -> NoSolutionOrAmbiguous { + NoSolutionOrAmbiguous::NoSolution + } +} + #[derive(Clone, Debug, Default, HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct DropckOutlivesResult<'tcx> { pub kinds: Vec>, diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index aa8094a60dd08..d6066677a4172 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -17,12 +17,12 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor}; use std::ops::ControlFlow; -use super::NoSolution; +use super::NoSolutionOrAmbiguous; pub use rustc_middle::traits::query::NormalizationResult; pub trait AtExt<'tcx> { - fn normalize(&self, value: T) -> Result, NoSolution> + fn normalize(&self, value: T) -> Result, NoSolutionOrAmbiguous> where T: TypeFoldable<'tcx>; } @@ -41,7 +41,7 @@ impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> { /// normalizing, but for now should be used only when we actually /// know that normalization will succeed, since error reporting /// and other details are still "under development". - fn normalize(&self, value: T) -> Result, NoSolution> + fn normalize(&self, value: T) -> Result, NoSolutionOrAmbiguous> where T: TypeFoldable<'tcx>, { @@ -96,7 +96,7 @@ impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> { std::any::type_name::(), normalizer.obligations, ); - result.map(|value| Normalized { value, obligations: normalizer.obligations }) + Ok(Normalized { value: result?, obligations: normalizer.obligations }) } } @@ -163,7 +163,7 @@ struct QueryNormalizer<'cx, 'tcx> { } impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { - type Error = NoSolution; + type Error = NoSolutionOrAmbiguous; fn tcx<'c>(&'c self) -> TyCtxt<'tcx> { self.infcx.tcx @@ -253,7 +253,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { let result = tcx.normalize_projection_ty(c_data)?; // We don't expect ambiguity. if result.is_ambiguous() { - bug!("unexpected ambiguity: {:?} {:?}", c_data, result); + return Err(NoSolutionOrAmbiguous::Ambiguous); } let InferOk { value: result, obligations } = self.infcx.instantiate_query_response_and_region_obligations( @@ -296,7 +296,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { let result = tcx.normalize_projection_ty(c_data)?; // We don't expect ambiguity. if result.is_ambiguous() { - bug!("unexpected ambiguity: {:?} {:?}", c_data, result); + return Err(NoSolutionOrAmbiguous::Ambiguous); } let InferOk { value: result, obligations } = self.infcx.instantiate_query_response_and_region_obligations( diff --git a/compiler/rustc_traits/src/dropck_outlives.rs b/compiler/rustc_traits/src/dropck_outlives.rs index d5a8ca5ea784a..cce9007c08360 100644 --- a/compiler/rustc_traits/src/dropck_outlives.rs +++ b/compiler/rustc_traits/src/dropck_outlives.rs @@ -131,8 +131,8 @@ fn dropck_outlives<'tcx>( // We don't actually expect to fail to normalize. // That implies a WF error somewhere else. - Err(NoSolution) => { - return Err(NoSolution); + Err(err) => { + return Err(err.expect_unambiguous()); } } } diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs index 2da64d73d34ac..d676f3faadc7f 100644 --- a/compiler/rustc_traits/src/normalize_erasing_regions.rs +++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs @@ -48,7 +48,7 @@ fn try_normalize_after_erasing_regions<'tcx, T: TypeFoldable<'tcx> + PartialEq + debug_assert!(!erased.needs_infer(), "{:?}", erased); Ok(erased) } - Err(NoSolution) => Err(NoSolution), + Err(err) => Err(err.expect_unambiguous()), } } diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs index bca7458ed332b..b06a4d9ef2910 100644 --- a/compiler/rustc_traits/src/type_op.rs +++ b/compiler/rustc_traits/src/type_op.rs @@ -218,8 +218,10 @@ where T: fmt::Debug + TypeFoldable<'tcx> + Lift<'tcx>, { let (param_env, Normalize { value }) = key.into_parts(); - let Normalized { value, obligations } = - infcx.at(&ObligationCause::dummy(), param_env).normalize(value)?; + let Normalized { value, obligations } = infcx + .at(&ObligationCause::dummy(), param_env) + .normalize(value) + .map_err(|err| err.expect_unambiguous())?; fulfill_cx.register_predicate_obligations(infcx, obligations); Ok(value) } diff --git a/src/test/rustdoc/issue-102835.rs b/src/test/rustdoc/issue-102835.rs new file mode 100644 index 0000000000000..253dd8d6ce6f8 --- /dev/null +++ b/src/test/rustdoc/issue-102835.rs @@ -0,0 +1,21 @@ +// compile-flags: -Znormalize-docs + +#![feature(type_alias_impl_trait)] + +trait Allocator { + type Buffer; +} + +struct DefaultAllocator; + +impl Allocator for DefaultAllocator { + type Buffer = (); +} + +type A = impl Fn(::Buffer); + +fn foo() -> A { + |_| () +} + +fn main() {} From dfff733cf214e1c03e2a7e7b83b01ee6ca0c6ac5 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 20 Oct 2022 17:57:07 +0000 Subject: [PATCH 2/3] Allow type op to delay ambiguity --- compiler/rustc_middle/src/traits/query.rs | 12 ++++++++++++ compiler/rustc_traits/src/type_op.rs | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index a943161efd1ef..5ea40868f0916 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -116,12 +116,24 @@ pub enum NoSolutionOrAmbiguous { } impl NoSolutionOrAmbiguous { + // Expect unambiguous errors only pub fn expect_unambiguous(self) -> NoSolution { match self { NoSolutionOrAmbiguous::NoSolution => NoSolution, NoSolutionOrAmbiguous::Ambiguous => bug!("unexpected ambiguity"), } } + + /// Delay an ambiguity as a `delay_span_bug`. + pub fn delay_ambiguous(self, tcx: TyCtxt<'_>, span: Span) -> NoSolution { + match self { + NoSolutionOrAmbiguous::NoSolution => NoSolution, + NoSolutionOrAmbiguous::Ambiguous => { + tcx.sess.delay_span_bug(span, "unexpected ambiguity"); + NoSolution + } + } + } } impl From for NoSolutionOrAmbiguous { diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs index b06a4d9ef2910..fa9c71d1d02fd 100644 --- a/compiler/rustc_traits/src/type_op.rs +++ b/compiler/rustc_traits/src/type_op.rs @@ -221,7 +221,7 @@ where let Normalized { value, obligations } = infcx .at(&ObligationCause::dummy(), param_env) .normalize(value) - .map_err(|err| err.expect_unambiguous())?; + .map_err(|err| err.delay_ambiguous(infcx.tcx, DUMMY_SP))?; fulfill_cx.register_predicate_obligations(infcx, obligations); Ok(value) } From 2802bd8876077debc4996f81619cf314dcdcf76a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 20 Oct 2022 18:32:11 +0000 Subject: [PATCH 3/3] Add tests for #103181 --- src/test/ui/impl-trait/issue-103181-1.rs | 85 ++++++++++++++++++++ src/test/ui/impl-trait/issue-103181-1.stderr | 12 +++ src/test/ui/impl-trait/issue-103181-2.rs | 29 +++++++ src/test/ui/impl-trait/issue-103181-2.stderr | 9 +++ 4 files changed, 135 insertions(+) create mode 100644 src/test/ui/impl-trait/issue-103181-1.rs create mode 100644 src/test/ui/impl-trait/issue-103181-1.stderr create mode 100644 src/test/ui/impl-trait/issue-103181-2.rs create mode 100644 src/test/ui/impl-trait/issue-103181-2.stderr diff --git a/src/test/ui/impl-trait/issue-103181-1.rs b/src/test/ui/impl-trait/issue-103181-1.rs new file mode 100644 index 0000000000000..197aedf9d98bc --- /dev/null +++ b/src/test/ui/impl-trait/issue-103181-1.rs @@ -0,0 +1,85 @@ +// edition:2021 + +mod hyper { + use std::{fmt::Debug, future::Future, marker::PhantomData, pin::Pin, task::Poll}; + + pub trait HttpBody { + type Error; + } + impl HttpBody for () { + //~^ ERROR not all trait items implemented, missing: `Error` + // don't implement `Error` here for the ICE + } + + pub struct Server(I, S); + + pub fn serve(_: S) -> Server { + todo!() + } + + impl Future for Server<(), S> + where + S: MakeServiceRef<(), (), ResBody = B>, + B: HttpBody, + B::Error: Debug, + { + type Output = (); + + fn poll(self: Pin<&mut Self>, _: &mut std::task::Context<'_>) -> Poll { + todo!() + } + } + + pub trait MakeServiceRef { + type ResBody; + } + + impl MakeServiceRef<(), ()> for T + where + T: for<'a> Service<&'a (), Response = S>, + S: Service<()>, + { + type ResBody = (); + } + + pub struct MakeServiceFn(pub F); + pub struct ServiceFn(pub PhantomData<(F, R)>); + + pub trait Service { + type Response; + } + + impl<'t, F, Ret, Target, Svc> Service<&'t Target> for MakeServiceFn + where + F: Fn() -> Ret, + Ret: Future>, + { + type Response = Svc; + } + + impl Service for ServiceFn + where + F: Fn() -> Ret, + Ret: Future>, + { + type Response = ResBody; + } +} + +async fn smarvice() -> Result<(), ()> { + Ok(()) +} + +fn service_fn(f: F) -> hyper::ServiceFn +where + F: Fn() -> S, +{ + hyper::ServiceFn(std::marker::PhantomData) +} + +async fn iceice() { + let service = hyper::MakeServiceFn(|| async { Ok::<_, ()>(service_fn(|| smarvice())) }); + hyper::serve::<(), _>(service).await; +} + +fn main() {} diff --git a/src/test/ui/impl-trait/issue-103181-1.stderr b/src/test/ui/impl-trait/issue-103181-1.stderr new file mode 100644 index 0000000000000..cd026607d52fc --- /dev/null +++ b/src/test/ui/impl-trait/issue-103181-1.stderr @@ -0,0 +1,12 @@ +error[E0046]: not all trait items implemented, missing: `Error` + --> $DIR/issue-103181-1.rs:9:5 + | +LL | type Error; + | ---------- `Error` from trait +LL | } +LL | impl HttpBody for () { + | ^^^^^^^^^^^^^^^^^^^^ missing `Error` in implementation + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0046`. diff --git a/src/test/ui/impl-trait/issue-103181-2.rs b/src/test/ui/impl-trait/issue-103181-2.rs new file mode 100644 index 0000000000000..b43ac45075e2b --- /dev/null +++ b/src/test/ui/impl-trait/issue-103181-2.rs @@ -0,0 +1,29 @@ +// edition:2021 + +trait SendFuture: Send { + type Output; +} + +impl SendFuture for Fut { + type Output = (); +} + +async fn broken_fut() { + ident_error; + //~^ ERROR cannot find value `ident_error` in this scope +} + +// triggers normalization of `::Output`, +// which requires `Fut: Send`. +fn normalize(_: Fut, _: Fut::Output) {} + +async fn iceice() +// <- async fn is necessary +where + A: Send, + B: Send, // <- a second bound +{ + normalize(broken_fut(), ()); +} + +fn main() {} diff --git a/src/test/ui/impl-trait/issue-103181-2.stderr b/src/test/ui/impl-trait/issue-103181-2.stderr new file mode 100644 index 0000000000000..5eb2dd9184bec --- /dev/null +++ b/src/test/ui/impl-trait/issue-103181-2.stderr @@ -0,0 +1,9 @@ +error[E0425]: cannot find value `ident_error` in this scope + --> $DIR/issue-103181-2.rs:12:5 + | +LL | ident_error; + | ^^^^^^^^^^^ not found in this scope + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0425`.