Gracefully fail to resolve associated items instead of delay_span_bug.

This commit is contained in:
Camille GILLOT 2022-05-01 11:03:14 +02:00
parent 532be942dd
commit 6cfe52c094
11 changed files with 99 additions and 87 deletions

View File

@ -1145,7 +1145,7 @@ rustc_queries! {
query codegen_fulfill_obligation(
key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)
) -> Result<&'tcx ImplSource<'tcx, ()>, ErrorGuaranteed> {
) -> Result<&'tcx ImplSource<'tcx, ()>, traits::CodegenObligationError> {
cache_on_disk_if { true }
desc { |tcx|
"checking if `{}` fulfills its obligations",

View File

@ -963,3 +963,21 @@ pub enum MethodViolationCode {
/// the method's receiver (`self` argument) can't be dispatched on
UndispatchableReceiver,
}
/// These are the error cases for `codegen_fulfill_obligation`.
#[derive(Copy, Clone, Debug, Hash, HashStable, Encodable, Decodable)]
pub enum CodegenObligationError {
/// Ambiguity can happen when monomorphizing during trans
/// expands to some humongous type that never occurred
/// statically -- this humongous type can then overflow,
/// leading to an ambiguous result. So report this as an
/// overflow bug, since I believe this is the only case
/// where ambiguity can result.
Ambiguity,
/// This can trigger when we probe for the source of a `'static` lifetime requirement
/// on a trait object: `impl Foo for dyn Trait {}` has an implicit `'static` bound.
/// This can also trigger when we have a global bound that is not actually satisfied,
/// but was included during typeck due to the trivial_bounds feature.
Unimplemented,
FulfillmentError,
}

View File

@ -3,13 +3,12 @@
// seems likely that they should eventually be merged into more
// general routines.
use crate::infer::{InferCtxt, TyCtxtInferExt};
use crate::infer::TyCtxtInferExt;
use crate::traits::{
FulfillmentContext, ImplSource, Obligation, ObligationCause, SelectionContext, TraitEngine,
Unimplemented,
};
use rustc_errors::ErrorGuaranteed;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::traits::CodegenObligationError;
use rustc_middle::ty::{self, TyCtxt};
/// Attempts to resolve an obligation to an `ImplSource`. The result is
@ -23,7 +22,7 @@ use rustc_middle::ty::{self, TyCtxt};
pub fn codegen_fulfill_obligation<'tcx>(
tcx: TyCtxt<'tcx>,
(param_env, trait_ref): (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>),
) -> Result<&'tcx ImplSource<'tcx, ()>, ErrorGuaranteed> {
) -> Result<&'tcx ImplSource<'tcx, ()>, CodegenObligationError> {
// Remove any references to regions; this helps improve caching.
let trait_ref = tcx.erase_regions(trait_ref);
// We expect the input to be fully normalized.
@ -40,37 +39,8 @@ pub fn codegen_fulfill_obligation<'tcx>(
let selection = match selcx.select(&obligation) {
Ok(Some(selection)) => selection,
Ok(None) => {
// Ambiguity can happen when monomorphizing during trans
// expands to some humongous type that never occurred
// statically -- this humongous type can then overflow,
// leading to an ambiguous result. So report this as an
// overflow bug, since I believe this is the only case
// where ambiguity can result.
let reported = infcx.tcx.sess.delay_span_bug(
rustc_span::DUMMY_SP,
&format!(
"encountered ambiguity selecting `{:?}` during codegen, presuming due to \
overflow or prior type error",
trait_ref
),
);
return Err(reported);
}
Err(Unimplemented) => {
// This can trigger when we probe for the source of a `'static` lifetime requirement
// on a trait object: `impl Foo for dyn Trait {}` has an implicit `'static` bound.
// This can also trigger when we have a global bound that is not actually satisfied,
// but was included during typeck due to the trivial_bounds feature.
let guar = infcx.tcx.sess.delay_span_bug(
rustc_span::DUMMY_SP,
&format!(
"Encountered error `Unimplemented` selecting `{:?}` during codegen",
trait_ref
),
);
return Err(guar);
}
Ok(None) => return Err(CodegenObligationError::Ambiguity),
Err(Unimplemented) => return Err(CodegenObligationError::Unimplemented),
Err(e) => {
bug!("Encountered error `{:?}` selecting `{:?}` during codegen", e, trait_ref)
}
@ -85,7 +55,17 @@ pub fn codegen_fulfill_obligation<'tcx>(
let impl_source = selection.map(|predicate| {
fulfill_cx.register_predicate_obligation(&infcx, predicate);
});
let impl_source = drain_fulfillment_cx_or_panic(&infcx, &mut fulfill_cx, impl_source);
// In principle, we only need to do this so long as `impl_source`
// contains unbound type parameters. It could be a slight
// optimization to stop iterating early.
let errors = fulfill_cx.select_all_or_error(&infcx);
if !errors.is_empty() {
return Err(CodegenObligationError::FulfillmentError);
}
let impl_source = infcx.resolve_vars_if_possible(impl_source);
let impl_source = infcx.tcx.erase_regions(impl_source);
// Opaque types may have gotten their hidden types constrained, but we can ignore them safely
// as they will get constrained elsewhere, too.
@ -95,42 +75,3 @@ pub fn codegen_fulfill_obligation<'tcx>(
Ok(&*tcx.arena.alloc(impl_source))
})
}
// # Global Cache
/// Finishes processes any obligations that remain in the
/// fulfillment context, and then returns the result with all type
/// variables removed and regions erased. Because this is intended
/// for use outside of type inference, if any errors occur,
/// it will panic. It is used during normalization and other cases
/// where processing the obligations in `fulfill_cx` may cause
/// type inference variables that appear in `result` to be
/// unified, and hence we need to process those obligations to get
/// the complete picture of the type.
fn drain_fulfillment_cx_or_panic<'tcx, T>(
infcx: &InferCtxt<'_, 'tcx>,
fulfill_cx: &mut FulfillmentContext<'tcx>,
result: T,
) -> T
where
T: TypeFoldable<'tcx>,
{
debug!("drain_fulfillment_cx_or_panic()");
// In principle, we only need to do this so long as `result`
// contains unbound type parameters. It could be a slight
// optimization to stop iterating early.
let errors = fulfill_cx.select_all_or_error(infcx);
if !errors.is_empty() {
infcx.tcx.sess.delay_span_bug(
rustc_span::DUMMY_SP,
&format!(
"Encountered errors `{:?}` resolving bounds outside of type inference",
errors
),
);
}
let result = infcx.resolve_vars_if_possible(result);
infcx.tcx.erase_regions(result)
}

View File

@ -1,6 +1,7 @@
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::traits::CodegenObligationError;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{self, Binder, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitor};
use rustc_span::{sym, DUMMY_SP};
@ -212,7 +213,22 @@ fn resolve_associated_item<'tcx>(
let mut bound_vars_collector = BoundVarsCollector::new();
trait_ref.visit_with(&mut bound_vars_collector);
let trait_binder = ty::Binder::bind_with_vars(trait_ref, bound_vars_collector.into_vars(tcx));
let vtbl = tcx.codegen_fulfill_obligation((param_env, trait_binder))?;
let vtbl = match tcx.codegen_fulfill_obligation((param_env, trait_binder)) {
Ok(vtbl) => vtbl,
Err(CodegenObligationError::Ambiguity) => {
let reported = tcx.sess.delay_span_bug(
tcx.def_span(trait_item_id),
&format!(
"encountered ambiguity selecting `{:?}` during codegen, presuming due to \
overflow or prior type error",
trait_binder
),
);
return Err(reported);
}
Err(CodegenObligationError::Unimplemented) => return Ok(None),
Err(CodegenObligationError::FulfillmentError) => return Ok(None),
};
// Now that we know which impl is being used, we can dispatch to
// the actual function:

View File

@ -24,6 +24,7 @@ fn writes_to_path<C>(cap: &C) {
writes_to_specific_path(&cap);
//~^ ERROR: the trait bound `(): _Contains<&C>` is not satisfied [E0277]
//~| ERROR: unconstrained generic constant
//~| ERROR: mismatched types [E0308]
}
fn writes_to_specific_path<C: Delegates<()>>(cap: &C) {}

View File

@ -18,7 +18,7 @@ note: required because of the requirements on the impl of `Delegates<()>` for `&
LL | impl<T, U> Delegates<U> for T where T: Contains<U, true> {}
| ^^^^^^^^^^^^ ^
note: required by a bound in `writes_to_specific_path`
--> $DIR/issue-85848.rs:29:31
--> $DIR/issue-85848.rs:30:31
|
LL | fn writes_to_specific_path<C: Delegates<()>>(cap: &C) {}
| ^^^^^^^^^^^^^ required by this bound in `writes_to_specific_path`
@ -43,11 +43,21 @@ note: required because of the requirements on the impl of `Delegates<()>` for `&
LL | impl<T, U> Delegates<U> for T where T: Contains<U, true> {}
| ^^^^^^^^^^^^ ^
note: required by a bound in `writes_to_specific_path`
--> $DIR/issue-85848.rs:29:31
--> $DIR/issue-85848.rs:30:31
|
LL | fn writes_to_specific_path<C: Delegates<()>>(cap: &C) {}
| ^^^^^^^^^^^^^ required by this bound in `writes_to_specific_path`
error: aborting due to 2 previous errors
error[E0308]: mismatched types
--> $DIR/issue-85848.rs:24:5
|
LL | writes_to_specific_path(&cap);
| ^^^^^^^^^^^^^^^^^^^^^^^ expected `true`, found `{ contains::<T, U>() }`
|
= note: expected type `true`
found type `{ contains::<T, U>() }`
For more information about this error, try `rustc --explain E0277`.
error: aborting due to 3 previous errors
Some errors have detailed explanations: E0277, E0308.
For more information about an error, try `rustc --explain E0277`.

View File

@ -15,6 +15,7 @@ where
fn unit_literals() {
z(" ");
//~^ ERROR: the trait bound `&str: X` is not satisfied
//~| ERROR: unconstrained generic constant
}
fn main() {}

View File

@ -15,6 +15,22 @@ LL | where
LL | T: X,
| ^ required by this bound in `z`
error: aborting due to previous error
error: unconstrained generic constant
--> $DIR/issue-86530.rs:16:5
|
LL | z(" ");
| ^
|
= help: try adding a `where` bound using this expression: `where [(); T::Y]:`
note: required by a bound in `z`
--> $DIR/issue-86530.rs:11:10
|
LL | fn z<T>(t: T)
| - required by a bound in this
...
LL | [(); T::Y]: ,
| ^^^^ required by this bound in `z`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0277`.

View File

@ -1,5 +1,6 @@
fn main() {
[1; <Multiply<Five, Five>>::VAL];
//~^ ERROR: constant expression depends on a generic parameter
}
trait TypeVal<T> {
const VAL: T;

View File

@ -1,5 +1,5 @@
error[E0412]: cannot find type `PhantomData` in this scope
--> $DIR/issue-77919.rs:9:9
--> $DIR/issue-77919.rs:10:9
|
LL | _n: PhantomData,
| ^^^^^^^^^^^ not found in this scope
@ -10,7 +10,7 @@ LL | use std::marker::PhantomData;
|
error[E0412]: cannot find type `VAL` in this scope
--> $DIR/issue-77919.rs:11:63
--> $DIR/issue-77919.rs:12:63
|
LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
| - ^^^ not found in this scope
@ -18,7 +18,7 @@ LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
| help: you might be missing a type parameter: `, VAL`
error[E0046]: not all trait items implemented, missing: `VAL`
--> $DIR/issue-77919.rs:11:1
--> $DIR/issue-77919.rs:12:1
|
LL | const VAL: T;
| ------------- `VAL` from trait
@ -26,7 +26,15 @@ LL | const VAL: T;
LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `VAL` in implementation
error: aborting due to 3 previous errors
error: constant expression depends on a generic parameter
--> $DIR/issue-77919.rs:2:9
|
LL | [1; <Multiply<Five, Five>>::VAL];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this may fail depending on what value the parameter takes
error: aborting due to 4 previous errors
Some errors have detailed explanations: E0046, E0412.
For more information about an error, try `rustc --explain E0046`.

View File

@ -1,5 +1,5 @@
// build-fail
//~^ overflow evaluating
//~^ ERROR overflow evaluating the requirement
fn main() {
let mut iter = 0u8..1;