Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Properly bubble up ambiguity in normalize query #102858

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions compiler/rustc_middle/src/traits/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,45 @@ impl<'tcx> From<TypeError<'tcx>> 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<NoSolution> for NoSolutionOrAmbiguous {
fn from(_: NoSolution) -> NoSolutionOrAmbiguous {
NoSolutionOrAmbiguous::NoSolution
}
}

impl<'tcx> From<TypeError<'tcx>> for NoSolutionOrAmbiguous {
fn from(_: TypeError<'tcx>) -> NoSolutionOrAmbiguous {
NoSolutionOrAmbiguous::NoSolution
}
}

#[derive(Clone, Debug, Default, HashStable, TypeFoldable, TypeVisitable, Lift)]
pub struct DropckOutlivesResult<'tcx> {
pub kinds: Vec<GenericArg<'tcx>>,
Expand Down
14 changes: 7 additions & 7 deletions compiler/rustc_trait_selection/src/traits/query/normalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
fn normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolutionOrAmbiguous>
where
T: TypeFoldable<'tcx>;
}
Expand All @@ -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<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
fn normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolutionOrAmbiguous>
where
T: TypeFoldable<'tcx>,
{
Expand Down Expand Up @@ -96,7 +96,7 @@ impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> {
std::any::type_name::<T>(),
normalizer.obligations,
);
result.map(|value| Normalized { value, obligations: normalizer.obligations })
Ok(Normalized { value: result?, obligations: normalizer.obligations })
}
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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(
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_traits/src/dropck_outlives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_traits/src/normalize_erasing_regions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()),
}
}

Expand Down
6 changes: 4 additions & 2 deletions compiler/rustc_traits/src/type_op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
21 changes: 21 additions & 0 deletions src/test/rustdoc/issue-102835.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// compile-flags: -Znormalize-docs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you name this file something more descriptive? Something like "normalize-ambiguous-item-not-fatal.rs"?


#![feature(type_alias_impl_trait)]

trait Allocator {
type Buffer;
}

struct DefaultAllocator;

impl<T> Allocator for DefaultAllocator {
type Buffer = ();
}

type A = impl Fn(<DefaultAllocator as Allocator>::Buffer);

fn foo() -> A {
|_| ()
}

fn main() {}
85 changes: 85 additions & 0 deletions src/test/ui/impl-trait/issue-103181-1.rs
Original file line number Diff line number Diff line change
@@ -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>(I, S);

pub fn serve<I, S>(_: S) -> Server<I, S> {
todo!()
}

impl<S, B> 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<Self::Output> {
todo!()
}
}

pub trait MakeServiceRef<Target, ReqBody> {
type ResBody;
}

impl<T, S> MakeServiceRef<(), ()> for T
where
T: for<'a> Service<&'a (), Response = S>,
S: Service<()>,
{
type ResBody = ();
}

pub struct MakeServiceFn<F>(pub F);
pub struct ServiceFn<F, R>(pub PhantomData<(F, R)>);

pub trait Service<Request> {
type Response;
}

impl<'t, F, Ret, Target, Svc> Service<&'t Target> for MakeServiceFn<F>
where
F: Fn() -> Ret,
Ret: Future<Output = Result<Svc, ()>>,
{
type Response = Svc;
}

impl<F, ReqBody, Ret, ResBody, E> Service<ReqBody> for ServiceFn<F, ReqBody>
where
F: Fn() -> Ret,
Ret: Future<Output = Result<ResBody, E>>,
{
type Response = ResBody;
}
}

async fn smarvice() -> Result<(), ()> {
Ok(())
}

fn service_fn<F, R, S>(f: F) -> hyper::ServiceFn<F, R>
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() {}
12 changes: 12 additions & 0 deletions src/test/ui/impl-trait/issue-103181-1.stderr
Original file line number Diff line number Diff line change
@@ -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`.
29 changes: 29 additions & 0 deletions src/test/ui/impl-trait/issue-103181-2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// edition:2021

trait SendFuture: Send {
type Output;
}

impl<Fut: Send> SendFuture for Fut {
type Output = ();
}

async fn broken_fut() {
ident_error;
//~^ ERROR cannot find value `ident_error` in this scope
}

// triggers normalization of `<Fut as SendFuture>::Output`,
// which requires `Fut: Send`.
fn normalize<Fut: SendFuture>(_: Fut, _: Fut::Output) {}

async fn iceice<A, B>()
// <- async fn is necessary
where
A: Send,
B: Send, // <- a second bound
{
normalize(broken_fut(), ());
}

fn main() {}
9 changes: 9 additions & 0 deletions src/test/ui/impl-trait/issue-103181-2.stderr
Original file line number Diff line number Diff line change
@@ -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`.