Skip to content

[WIP] change overflow to be non-fatal #104534

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

Closed
wants to merge 5 commits into from
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
16 changes: 10 additions & 6 deletions compiler/rustc_codegen_llvm/src/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1745,19 +1745,23 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#,

match in_elem.kind() {
ty::RawPtr(p) => {
let (metadata, check_sized) = p.ty.ptr_metadata_ty(bx.tcx, |ty| {
bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty)
});
let (metadata, check_sized) =
p.ty.ptr_metadata_ty(bx.tcx, |ty| {
Ok(bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty))
})
.unwrap();
assert!(!check_sized); // we are in codegen, so we shouldn't see these types
require!(metadata.is_unit(), "cannot cast fat pointer `{}`", in_elem)
}
_ => return_error!("expected pointer, got `{}`", in_elem),
}
match out_elem.kind() {
ty::RawPtr(p) => {
let (metadata, check_sized) = p.ty.ptr_metadata_ty(bx.tcx, |ty| {
bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty)
});
let (metadata, check_sized) =
p.ty.ptr_metadata_ty(bx.tcx, |ty| {
Ok(bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty))
})
.unwrap();
assert!(!check_sized); // we are in codegen, so we shouldn't see these types
require!(metadata.is_unit(), "cannot cast to fat pointer `{}`", out_elem)
}
Expand Down
20 changes: 11 additions & 9 deletions compiler/rustc_const_eval/src/const_eval/valtrees.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,15 +177,17 @@ fn get_info_on_unsized_field<'tcx>(
tcx: TyCtxt<'tcx>,
) -> (Ty<'tcx>, usize) {
let mut last_valtree = valtree;
let tail = tcx.struct_tail_with_normalize(
ty,
|ty| ty,
|| {
let branches = last_valtree.unwrap_branch();
last_valtree = branches[branches.len() - 1];
debug!(?branches, ?last_valtree);
},
);
let tail = tcx
.struct_tail_with_normalize(
ty,
|ty| Ok(ty),
|| {
let branches = last_valtree.unwrap_branch();
last_valtree = branches[branches.len() - 1];
debug!(?branches, ?last_valtree);
},
)
.unwrap();
let unsized_inner_ty = match tail.kind() {
ty::Slice(t) => *t,
ty::Str => tail,
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_const_eval/src/transform/check_consts/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -770,14 +770,14 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
}

match implsrc {
Ok(Some(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => {
Ok(Ok(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => {
debug!(
"const_trait_impl: provided {:?} via where-clause in {:?}",
trait_ref, param_env
);
return;
}
Ok(Some(ImplSource::UserDefined(data))) => {
Ok(Ok(ImplSource::UserDefined(data))) => {
let callee_name = tcx.item_name(callee);
if let Some(&did) = tcx
.associated_item_def_ids(data.impl_def_id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
let mut selcx = SelectionContext::new(&infcx);
let implsrc = selcx.select(&obligation);

if let Ok(Some(ImplSource::UserDefined(data))) = implsrc {
if let Ok(Ok(ImplSource::UserDefined(data))) = implsrc {
let span = tcx.def_span(data.impl_def_id);
err.span_note(span, "impl defined here, but it is not `const`");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ impl Qualif for NeedsNonConstDrop {

let infcx = cx.tcx.infer_ctxt().build();
let mut selcx = SelectionContext::new(&infcx);
let Some(impl_src) = selcx.select(&obligation).ok().flatten() else {
let Ok(Ok(impl_src)) = selcx.select(&obligation) else {
// If we couldn't select a const destruct candidate, then it's bad
return true;
};
Expand Down
19 changes: 16 additions & 3 deletions compiler/rustc_data_structures/src/obligation_forest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ pub trait ObligationProcessor {
obligation: &mut Self::Obligation,
) -> ProcessResult<Self::Obligation, Self::Error>;

fn update_obligation_depth(
&mut self,
obligation: &Self::Obligation,
child: &mut Self::Obligation,
);

/// As we do the cycle check, we invoke this callback when we
/// encounter an actual cycle. `cycle` is an iterator that starts
/// at the start of the cycle in the stack and walks **toward the
Expand Down Expand Up @@ -375,13 +381,16 @@ impl<O: ForestObligation> ObligationForest<O> {
}

/// Converts all remaining obligations to the given error.
pub fn to_errors<E: Clone>(&mut self, error: E) -> Vec<Error<O, E>> {
pub fn to_errors<E>(&mut self, mut mk_error: impl FnMut(&O) -> E) -> Vec<Error<O, E>> {
let errors = self
.nodes
.iter()
.enumerate()
.filter(|(_index, node)| node.state.get() == NodeState::Pending)
.map(|(index, _node)| Error { error: error.clone(), backtrace: self.error_at(index) })
.map(|(index, node)| Error {
error: mk_error(&node.obligation),
backtrace: self.error_at(index),
})
.collect();

self.compress(|_| assert!(false));
Expand Down Expand Up @@ -447,11 +456,15 @@ impl<O: ForestObligation> ObligationForest<O> {
ProcessResult::Unchanged => {
// No change in state.
}
ProcessResult::Changed(children) => {
ProcessResult::Changed(mut children) => {
// We are not (yet) stalled.
has_changed = true;
node.state.set(NodeState::Success);

for child in &mut children {
processor.update_obligation_depth(&node.obligation, child);
}

for child in children {
let st = self.register_obligation_at(child, Some(index));
if let Err(()) = st {
Expand Down
10 changes: 6 additions & 4 deletions compiler/rustc_data_structures/src/obligation_forest/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ where
true
}

fn update_obligation_depth(&mut self, _: &Self::Obligation, _: &mut Self::Obligation) {}

fn process_obligation(
&mut self,
obligation: &mut Self::Obligation,
Expand Down Expand Up @@ -256,7 +258,7 @@ fn to_errors_no_throw() {
));
assert_eq!(ok.len(), 0);
assert_eq!(err.len(), 0);
let errors = forest.to_errors(());
let errors = forest.to_errors(|_| ());
assert_eq!(errors[0].backtrace, vec!["A.1", "A"]);
assert_eq!(errors[1].backtrace, vec!["A.2", "A"]);
assert_eq!(errors[2].backtrace, vec!["A.3", "A"]);
Expand Down Expand Up @@ -308,7 +310,7 @@ fn diamond() {
assert_eq!(ok, vec!["A", "A.1", "A.2", "D"]);
assert_eq!(err.len(), 0);

let errors = forest.to_errors(());
let errors = forest.to_errors(|_| ());
assert_eq!(errors.len(), 0);

forest.register_obligation("A'");
Expand Down Expand Up @@ -353,7 +355,7 @@ fn diamond() {
vec![super::Error { error: "operation failed", backtrace: vec!["D'", "A'.1", "A'"] }]
);

let errors = forest.to_errors(());
let errors = forest.to_errors(|_| ());
assert_eq!(errors.len(), 0);
}

Expand Down Expand Up @@ -446,7 +448,7 @@ fn orphan() {
assert_eq!(ok.len(), 0);
assert_eq!(err, vec![super::Error { error: "D is dead", backtrace: vec!["D"] }]);

let errors = forest.to_errors(());
let errors = forest.to_errors(|_| ());
assert_eq!(errors.len(), 0);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::specialization_graph::Node;
use rustc_infer::traits::Overflow;
use rustc_middle::ty::subst::{GenericArg, InternalSubsts, SubstsRef};
use rustc_middle::ty::trait_def::TraitSpecializationKind;
use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
Expand Down Expand Up @@ -377,7 +378,9 @@ fn check_predicates<'tcx>(
0,
arg,
span,
true,
)
.unwrap_or_else(|Overflow| bug!("impossible non-fatal overflow"))
.unwrap();

assert!(!obligations.needs_infer());
Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_hir_typeck/src/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
};
match selcx.select(&obligation.with(trait_pred)) {
// Uncertain or unimplemented.
Ok(None) => {
Ok(Err(true)) => self.err_ctxt().report_overflow_error(&obligation, true),
Ok(Err(false)) => {
if trait_pred.def_id() == unsize_did {
let trait_pred = self.resolve_vars_if_possible(trait_pred);
let self_ty = trait_pred.skip_binder().self_ty();
Expand Down Expand Up @@ -712,7 +713,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
// be silent, as it causes a type mismatch later.
}

Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()),
Ok(Ok(impl_source)) => queue.extend(impl_source.nested_obligations()),
}
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2159,7 +2159,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}),
);
match SelectionContext::new(&self).select(&obligation) {
Ok(Some(traits::ImplSource::UserDefined(impl_source))) => {
Ok(Ok(traits::ImplSource::UserDefined(impl_source))) => {
Some(impl_source.impl_def_id)
}
_ => None,
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1447,7 +1447,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
.define_opaque_types(false)
.sup(candidate.xform_self_ty, self_ty);
match self.select_trait_candidate(trait_ref) {
Ok(Some(traits::ImplSource::UserDefined(ref impl_data))) => {
Ok(Ok(traits::ImplSource::UserDefined(ref impl_data))) => {
// If only a single impl matches, make the error message point
// to that impl.
CandidateSource::Impl(impl_data.impl_def_id)
Expand Down Expand Up @@ -1566,7 +1566,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
if self.probe(|_| {
match self.select_trait_candidate(trait_ref) {
Err(_) => return true,
Ok(Some(impl_source))
Ok(Ok(impl_source))
if !impl_source.borrow_nested_obligations().is_empty() =>
{
for obligation in impl_source.borrow_nested_obligations() {
Expand Down
8 changes: 0 additions & 8 deletions compiler/rustc_infer/src/traits/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,6 @@ use super::FulfillmentError;
use super::{ObligationCause, PredicateObligation};

pub trait TraitEngine<'tcx>: 'tcx {
fn normalize_projection_type(
&mut self,
infcx: &InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
projection_ty: ty::ProjectionTy<'tcx>,
cause: ObligationCause<'tcx>,
) -> Ty<'tcx>;

/// Requires that `ty` must implement the trait with `def_id` in
/// the given environment. This trait must not have any type
/// parameters (except for `Self`).
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_infer/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ pub struct FulfillmentError<'tcx> {
pub root_obligation: PredicateObligation<'tcx>,
}

#[derive(Clone)]
#[derive(Debug, Clone)]
pub enum FulfillmentErrorCode<'tcx> {
/// Inherently impossible to fulfill; this trait is implemented if and only if it is already implemented.
CodeCycle(Vec<Obligation<'tcx, ty::Predicate<'tcx>>>),
Expand All @@ -120,6 +120,7 @@ pub enum FulfillmentErrorCode<'tcx> {
CodeSubtypeError(ExpectedFound<Ty<'tcx>>, TypeError<'tcx>), // always comes from a SubtypePredicate
CodeConstEquateError(ExpectedFound<Const<'tcx>>, TypeError<'tcx>),
CodeAmbiguity,
CodeOverflow,
}

impl<'tcx, O> Obligation<'tcx, O> {
Expand Down
8 changes: 7 additions & 1 deletion compiler/rustc_infer/src/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ impl<'tcx> ProjectionCacheKey<'tcx> {
pub enum ProjectionCacheEntry<'tcx> {
InProgress,
Ambiguous,
Overflow,
Recur,
Error,
NormalizedTy {
Expand Down Expand Up @@ -184,7 +185,7 @@ impl<'tcx> ProjectionCache<'_, 'tcx> {
key, value
);
let mut map = self.map();
if let Some(ProjectionCacheEntry::Recur) = map.get(&key) {
if let Some(ProjectionCacheEntry::Recur | ProjectionCacheEntry::Overflow) = map.get(&key) {
debug!("Not overwriting Recur");
return;
}
Expand Down Expand Up @@ -223,6 +224,11 @@ impl<'tcx> ProjectionCache<'_, 'tcx> {
})
}

pub fn overflow(&mut self, key: ProjectionCacheKey<'tcx>) {
let fresh = self.map().insert(key, ProjectionCacheEntry::Overflow);
assert!(!fresh, "never started projecting `{:?}", key);
}

/// Indicates that trying to normalize `key` resulted in
/// ambiguity. No point in trying it again then until we gain more
/// type information (in which case, the "fully resolved" key will
Expand Down
17 changes: 0 additions & 17 deletions compiler/rustc_infer/src/traits/structural_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,6 @@ impl<'tcx> fmt::Debug for traits::FulfillmentError<'tcx> {
}
}

impl<'tcx> fmt::Debug for traits::FulfillmentErrorCode<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
super::CodeSelectionError(ref e) => write!(f, "{:?}", e),
super::CodeProjectionError(ref e) => write!(f, "{:?}", e),
super::CodeSubtypeError(ref a, ref b) => {
write!(f, "CodeSubtypeError({:?}, {:?})", a, b)
}
super::CodeConstEquateError(ref a, ref b) => {
write!(f, "CodeConstEquateError({:?}, {:?})", a, b)
}
super::CodeAmbiguity => write!(f, "Ambiguity"),
super::CodeCycle(ref cycle) => write!(f, "Cycle({:?})", cycle),
}
}
}

impl<'tcx> fmt::Debug for traits::MismatchedProjectionTypes<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "MismatchedProjectionTypes({:?})", self.err)
Expand Down
22 changes: 5 additions & 17 deletions compiler/rustc_middle/src/infer/canonical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,30 +237,18 @@ pub enum Certainty {
/// canonical form will be different, making this a distinct
/// query.
Ambiguous,
}

impl Certainty {
pub fn is_proven(&self) -> bool {
match self {
Certainty::Proven => true,
Certainty::Ambiguous => false,
}
}
Overflow,
}

impl<'tcx, R> QueryResponse<'tcx, R> {
pub fn is_proven(&self) -> bool {
self.certainty.is_proven()
pub fn certainty(&self) -> Certainty {
self.certainty
}
}

impl<'tcx, R> Canonical<'tcx, QueryResponse<'tcx, R>> {
pub fn is_proven(&self) -> bool {
self.value.is_proven()
}

pub fn is_ambiguous(&self) -> bool {
!self.is_proven()
pub fn certainty(&self) -> Certainty {
self.value.certainty()
}
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1907,7 +1907,7 @@ rustc_queries! {
/// `infcx.predicate_must_hold()` instead.
query evaluate_obligation(
goal: CanonicalPredicateGoal<'tcx>
) -> Result<traits::EvaluationResult, traits::OverflowError> {
) -> traits::EvaluationResult {
desc { "evaluating trait selection obligation `{}`", goal.value.value }
}

Expand Down
Loading