diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index fb152b63f6344..5ea40868f0916 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -109,6 +109,45 @@ impl<'tcx> From> for NoSolution { } } +#[derive(Copy, Clone, Debug, HashStable)] +pub enum NoSolutionOrAmbiguous { + NoSolution, + Ambiguous, +} + +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 { + 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..fa9c71d1d02fd 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.delay_ambiguous(infcx.tcx, DUMMY_SP))?; 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() {} 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`.