Skip to content

Commit 669b833

Browse files
committed
Auto merge of rust-lang#141135 - compiler-errors:fast-path-2, r=<try>
Fast path for processing some obligations in the new solver (volume 2) r? `@ghost`
2 parents a43b8d1 + 57130ef commit 669b833

File tree

8 files changed

+122
-11
lines changed

8 files changed

+122
-11
lines changed

compiler/rustc_middle/src/ty/sty.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1883,9 +1883,9 @@ impl<'tcx> Ty<'tcx> {
18831883
// Needs normalization or revealing to determine, so no is the safe answer.
18841884
ty::Alias(..) => false,
18851885

1886-
ty::Param(..) | ty::Infer(..) | ty::Error(..) => false,
1886+
ty::Param(..) | ty::Placeholder(..) | ty::Infer(..) | ty::Error(..) => false,
18871887

1888-
ty::Bound(..) | ty::Placeholder(..) => {
1888+
ty::Bound(..) => {
18891889
bug!("`is_trivially_pure_clone_copy` applied to unexpected type: {:?}", self);
18901890
}
18911891
}

compiler/rustc_next_trait_solver/src/delegate.rs

+6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
1717
where
1818
V: TypeFoldable<Self::Interner>;
1919

20+
fn compute_goal_fast_path(
21+
&self,
22+
goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>,
23+
span: <Self::Interner as Interner>::Span,
24+
) -> Option<()>;
25+
2026
fn fresh_var_for_kind_with_span(
2127
&self,
2228
arg: <Self::Interner as Interner>::GenericArg,

compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use std::iter;
1313

1414
use rustc_index::IndexVec;
15+
use rustc_type_ir::data_structures::HashSet;
1516
use rustc_type_ir::inherent::*;
1617
use rustc_type_ir::relate::solver_relating::RelateExt;
1718
use rustc_type_ir::{
@@ -158,10 +159,12 @@ where
158159
self.compute_external_query_constraints(certainty, normalization_nested_goals);
159160
let (var_values, mut external_constraints) = (self.var_values, external_constraints)
160161
.fold_with(&mut EagerResolver::new(self.delegate));
161-
// Remove any trivial region constraints once we've resolved regions
162-
external_constraints
163-
.region_constraints
164-
.retain(|outlives| outlives.0.as_region().is_none_or(|re| re != outlives.1));
162+
163+
// Remove any trivial or duplicated region constraints once we've resolved regions
164+
let mut unique = HashSet::default();
165+
external_constraints.region_constraints.retain(|outlives| {
166+
outlives.0.as_region().is_none_or(|re| re != outlives.1) && unique.insert(*outlives)
167+
});
165168

166169
let canonical = Canonicalizer::canonicalize_response(
167170
self.delegate,

compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,11 @@ where
603603
// If this loop did not result in any progress, what's our final certainty.
604604
let mut unchanged_certainty = Some(Certainty::Yes);
605605
for (source, goal) in mem::take(&mut self.nested_goals) {
606+
if let Some(()) = self.delegate.compute_goal_fast_path(goal, self.origin_span) {
607+
unchanged_certainty = None;
608+
continue;
609+
}
610+
606611
// We treat normalizes-to goals specially here. In each iteration we take the
607612
// RHS of the projection, replace it with a fresh inference variable, and only
608613
// after evaluating that goal do we equate the fresh inference variable with the

compiler/rustc_trait_selection/src/solve/delegate.rs

+49-2
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
use std::ops::Deref;
22

33
use rustc_data_structures::fx::FxHashSet;
4+
use rustc_hir::LangItem;
45
use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
56
use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
67
use rustc_infer::infer::canonical::{
78
Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarInfo, CanonicalVarValues,
89
};
9-
use rustc_infer::infer::{InferCtxt, RegionVariableOrigin, TyCtxtInferExt};
10+
use rustc_infer::infer::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TyCtxtInferExt};
1011
use rustc_infer::traits::solve::Goal;
1112
use rustc_middle::traits::query::NoSolution;
1213
use rustc_middle::traits::solve::Certainty;
1314
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitableExt as _, TypingMode};
1415
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
1516

16-
use crate::traits::{EvaluateConstErr, specialization_graph};
17+
use crate::traits::{EvaluateConstErr, ObligationCause, specialization_graph};
1718

1819
#[repr(transparent)]
1920
pub struct SolverDelegate<'tcx>(InferCtxt<'tcx>);
@@ -55,6 +56,52 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
5556
(SolverDelegate(infcx), value, vars)
5657
}
5758

59+
fn compute_goal_fast_path(
60+
&self,
61+
goal: Goal<'tcx, ty::Predicate<'tcx>>,
62+
span: Span,
63+
) -> Option<()> {
64+
let pred = goal.predicate.kind();
65+
match pred.no_bound_vars()? {
66+
ty::PredicateKind::DynCompatible(def_id) if self.0.tcx.is_dyn_compatible(def_id) => {
67+
Some(())
68+
}
69+
ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(outlives)) => {
70+
self.0.sub_regions(
71+
SubregionOrigin::RelateRegionParamBound(span, None),
72+
outlives.1,
73+
outlives.0,
74+
);
75+
Some(())
76+
}
77+
ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(outlives)) => {
78+
self.0.register_region_obligation_with_cause(
79+
outlives.0,
80+
outlives.1,
81+
&ObligationCause::dummy_with_span(span),
82+
);
83+
84+
Some(())
85+
}
86+
ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) => {
87+
match self.0.tcx.as_lang_item(trait_pred.def_id()) {
88+
Some(LangItem::Sized)
89+
if trait_pred.self_ty().is_trivially_sized(self.0.tcx) =>
90+
{
91+
Some(())
92+
}
93+
Some(LangItem::Copy | LangItem::Clone)
94+
if trait_pred.self_ty().is_trivially_pure_clone_copy() =>
95+
{
96+
Some(())
97+
}
98+
_ => None,
99+
}
100+
}
101+
_ => None,
102+
}
103+
}
104+
58105
fn fresh_var_for_kind_with_span(
59106
&self,
60107
arg: ty::GenericArg<'tcx>,

compiler/rustc_trait_selection/src/solve/fulfill.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use rustc_infer::traits::{
1212
use rustc_middle::ty::{
1313
self, DelayedSet, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, TypingMode,
1414
};
15+
use rustc_next_trait_solver::delegate::SolverDelegate as _;
1516
use rustc_next_trait_solver::solve::{GenerateProofTree, HasChanged, SolverDelegateEvalExt as _};
1617
use rustc_span::Span;
1718
use tracing::instrument;
@@ -172,7 +173,13 @@ where
172173
}
173174

174175
let goal = obligation.as_goal();
175-
let result = <&SolverDelegate<'tcx>>::from(infcx)
176+
let delegate = <&SolverDelegate<'tcx>>::from(infcx);
177+
if let Some(()) = delegate.compute_goal_fast_path(goal, obligation.cause.span) {
178+
has_changed = true;
179+
continue;
180+
}
181+
182+
let result = delegate
176183
.evaluate_root_goal(goal, GenerateProofTree::No, obligation.cause.span)
177184
.0;
178185
self.inspect_evaluated_obligation(infcx, &obligation, &result);

compiler/rustc_trait_selection/src/traits/mod.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -701,9 +701,15 @@ pub fn impossible_predicates<'tcx>(tcx: TyCtxt<'tcx>, predicates: Vec<ty::Clause
701701
let obligation = Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate);
702702
ocx.register_obligation(obligation);
703703
}
704-
let errors = ocx.select_all_or_error();
705704

706-
if !errors.is_empty() {
705+
// Use `select_where_possible` to only return impossible for true errors,
706+
// and not ambiguities or overflows. Since the new trait solver forces
707+
// some currently undetected overlap between `dyn Trait: Trait` built-in
708+
// vs user-written impls to AMBIGUOUS, this may return ambiguity even
709+
// with no infer vars. There may also be ways to encounter ambiguity due
710+
// to post-mono overflow.
711+
let true_errors = ocx.select_where_possible();
712+
if !true_errors.is_empty() {
707713
return true;
708714
}
709715

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// In this example below, we have two overlapping candidates for `dyn Q: Q`.
2+
// Specifically, the user written impl for `<dyn Q as Mirror>::Assoc` and the
3+
// built-in impl for object types. Since they differ by their region responses,
4+
// the goal is ambiguous. This affects codegen since impossible obligations
5+
// for method dispatch will lead to a segfault, since we end up emitting dummy
6+
// call vtable offsets due to <https://github.com/rust-lang/rust/pull/136311>.
7+
8+
// Test for <https://github.com/rust-lang/rust/issues/141119>.
9+
10+
//@ run-pass
11+
12+
trait Mirror {
13+
type Assoc: ?Sized;
14+
}
15+
impl<T: ?Sized> Mirror for T {
16+
type Assoc = T;
17+
}
18+
19+
trait Q: 'static {
20+
fn q(&self);
21+
}
22+
23+
impl Q for i32 {
24+
fn q(&self) { println!("i32"); }
25+
}
26+
27+
impl Q for <dyn Q as Mirror>::Assoc where Self: 'static {
28+
fn q(&self) { println!("dyn Q"); }
29+
}
30+
31+
fn foo<T: Q + ?Sized>(t: &T) {
32+
t.q();
33+
}
34+
35+
fn main() {
36+
foo(&1 as &dyn Q);
37+
}

0 commit comments

Comments
 (0)