Skip to content

Commit bbb6459

Browse files
Merge pull request #20721 from ChayimFriedman2/fix-never
fix: Implement fallback properly
2 parents 993db83 + 6b133c6 commit bbb6459

File tree

16 files changed

+770
-230
lines changed

16 files changed

+770
-230
lines changed

Cargo.lock

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ tracing-subscriber = { version = "0.3.20", default-features = false, features =
170170
triomphe = { version = "0.1.14", default-features = false, features = ["std"] }
171171
url = "2.5.4"
172172
xshell = "0.2.7"
173+
petgraph = { version = "0.8.2", default-features = false }
173174

174175
# We need to freeze the version of the crate, as the raw-api feature is considered unstable
175176
dashmap = { version = "=6.1.0", features = ["raw-api", "inline"] }

crates/hir-ty/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ rustc_apfloat = "0.2.3"
3434
query-group.workspace = true
3535
salsa.workspace = true
3636
salsa-macros.workspace = true
37+
petgraph.workspace = true
3738

3839
ra-ap-rustc_abi.workspace = true
3940
ra-ap-rustc_index.workspace = true

crates/hir-ty/src/consteval.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ pub(crate) fn eval_to_const(
327327
debruijn: DebruijnIndex,
328328
) -> Const {
329329
let db = ctx.db;
330-
let infer = ctx.clone().resolve_all();
330+
let infer = ctx.fixme_resolve_all_clone();
331331
fn has_closure(body: &Body, expr: ExprId) -> bool {
332332
if matches!(body[expr], Expr::Closure { .. }) {
333333
return true;

crates/hir-ty/src/consteval/tests.rs

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ fn check_fail(
3636
error: impl FnOnce(ConstEvalError) -> bool,
3737
) {
3838
let (db, file_id) = TestDB::with_single_file(ra_fixture);
39-
match eval_goal(&db, file_id) {
39+
salsa::attach(&db, || match eval_goal(&db, file_id) {
4040
Ok(_) => panic!("Expected fail, but it succeeded"),
4141
Err(e) => {
42-
assert!(error(simplify(e.clone())), "Actual error was: {}", pretty_print_err(e, db))
42+
assert!(error(simplify(e.clone())), "Actual error was: {}", pretty_print_err(e, &db))
4343
}
44-
}
44+
})
4545
}
4646

4747
#[track_caller]
@@ -79,36 +79,38 @@ fn check_answer(
7979
check: impl FnOnce(&[u8], &MemoryMap<'_>),
8080
) {
8181
let (db, file_ids) = TestDB::with_many_files(ra_fixture);
82-
let file_id = *file_ids.last().unwrap();
83-
let r = match eval_goal(&db, file_id) {
84-
Ok(t) => t,
85-
Err(e) => {
86-
let err = pretty_print_err(e, db);
87-
panic!("Error in evaluating goal: {err}");
88-
}
89-
};
90-
match &r.data(Interner).value {
91-
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
92-
ConstScalar::Bytes(b, mm) => {
93-
check(b, mm);
82+
salsa::attach(&db, || {
83+
let file_id = *file_ids.last().unwrap();
84+
let r = match eval_goal(&db, file_id) {
85+
Ok(t) => t,
86+
Err(e) => {
87+
let err = pretty_print_err(e, &db);
88+
panic!("Error in evaluating goal: {err}");
9489
}
95-
x => panic!("Expected number but found {x:?}"),
96-
},
97-
_ => panic!("result of const eval wasn't a concrete const"),
98-
}
90+
};
91+
match &r.data(Interner).value {
92+
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
93+
ConstScalar::Bytes(b, mm) => {
94+
check(b, mm);
95+
}
96+
x => panic!("Expected number but found {x:?}"),
97+
},
98+
_ => panic!("result of const eval wasn't a concrete const"),
99+
}
100+
});
99101
}
100102

101-
fn pretty_print_err(e: ConstEvalError, db: TestDB) -> String {
103+
fn pretty_print_err(e: ConstEvalError, db: &TestDB) -> String {
102104
let mut err = String::new();
103105
let span_formatter = |file, range| format!("{file:?} {range:?}");
104106
let display_target =
105-
DisplayTarget::from_crate(&db, *db.all_crates().last().expect("no crate graph present"));
107+
DisplayTarget::from_crate(db, *db.all_crates().last().expect("no crate graph present"));
106108
match e {
107109
ConstEvalError::MirLowerError(e) => {
108-
e.pretty_print(&mut err, &db, span_formatter, display_target)
110+
e.pretty_print(&mut err, db, span_formatter, display_target)
109111
}
110112
ConstEvalError::MirEvalError(e) => {
111-
e.pretty_print(&mut err, &db, span_formatter, display_target)
113+
e.pretty_print(&mut err, db, span_formatter, display_target)
112114
}
113115
}
114116
.unwrap();

crates/hir-ty/src/consteval_nextsolver.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ pub(crate) fn const_eval_discriminant_variant(
222222
// and make this function private. See the fixme comment on `InferenceContext::resolve_all`.
223223
pub(crate) fn eval_to_const<'db>(expr: ExprId, ctx: &mut InferenceContext<'db>) -> Const<'db> {
224224
let interner = DbInterner::new_with(ctx.db, None, None);
225-
let infer = ctx.clone().resolve_all();
225+
let infer = ctx.fixme_resolve_all_clone();
226226
fn has_closure(body: &Body, expr: ExprId) -> bool {
227227
if matches!(body[expr], Expr::Closure { .. }) {
228228
return true;

crates/hir-ty/src/infer.rs

Lines changed: 76 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub(crate) mod closure;
1919
mod coerce;
2020
pub(crate) mod diagnostics;
2121
mod expr;
22+
mod fallback;
2223
mod mutability;
2324
mod pat;
2425
mod path;
@@ -53,16 +54,16 @@ use indexmap::IndexSet;
5354
use intern::sym;
5455
use la_arena::{ArenaMap, Entry};
5556
use rustc_hash::{FxHashMap, FxHashSet};
57+
use rustc_type_ir::inherent::Ty as _;
5658
use stdx::{always, never};
5759
use triomphe::Arc;
5860

59-
use crate::db::InternedClosureId;
6061
use crate::{
6162
AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, ImplTraitId, ImplTraitIdx,
6263
IncorrectGenericsLenKind, Interner, Lifetime, OpaqueTyId, ParamLoweringMode,
6364
PathLoweringDiagnostic, ProjectionTy, Substitution, TargetFeatures, TraitEnvironment, Ty,
6465
TyBuilder, TyExt,
65-
db::HirDatabase,
66+
db::{HirDatabase, InternedClosureId},
6667
fold_tys,
6768
generics::Generics,
6869
infer::{
@@ -75,6 +76,7 @@ use crate::{
7576
mir::MirSpan,
7677
next_solver::{
7778
self, DbInterner,
79+
infer::{DefineOpaqueTypes, traits::ObligationCause},
7880
mapping::{ChalkToNextSolver, NextSolverToChalk},
7981
},
8082
static_lifetime, to_assoc_type_id,
@@ -138,6 +140,20 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
138140

139141
ctx.infer_mut_body();
140142

143+
ctx.type_inference_fallback();
144+
145+
// Comment from rustc:
146+
// Even though coercion casts provide type hints, we check casts after fallback for
147+
// backwards compatibility. This makes fallback a stronger type hint than a cast coercion.
148+
let cast_checks = std::mem::take(&mut ctx.deferred_cast_checks);
149+
for mut cast in cast_checks.into_iter() {
150+
if let Err(diag) = cast.check(&mut ctx) {
151+
ctx.diagnostics.push(diag);
152+
}
153+
}
154+
155+
ctx.table.select_obligations_where_possible();
156+
141157
ctx.infer_closures();
142158

143159
Arc::new(ctx.resolve_all())
@@ -165,7 +181,6 @@ pub(crate) fn normalize(db: &dyn HirDatabase, trait_env: Arc<TraitEnvironment<'_
165181

166182
let ty_with_vars = table.normalize_associated_types_in(ty);
167183
table.select_obligations_where_possible();
168-
table.propagate_diverging_flag();
169184
table.resolve_completely(ty_with_vars)
170185
}
171186

@@ -686,6 +701,25 @@ impl Index<BindingId> for InferenceResult {
686701
}
687702
}
688703

704+
#[derive(Debug, Clone)]
705+
struct InternedStandardTypesNextSolver<'db> {
706+
unit: crate::next_solver::Ty<'db>,
707+
never: crate::next_solver::Ty<'db>,
708+
i32: crate::next_solver::Ty<'db>,
709+
f64: crate::next_solver::Ty<'db>,
710+
}
711+
712+
impl<'db> InternedStandardTypesNextSolver<'db> {
713+
fn new(interner: DbInterner<'db>) -> Self {
714+
Self {
715+
unit: crate::next_solver::Ty::new_unit(interner),
716+
never: crate::next_solver::Ty::new(interner, crate::next_solver::TyKind::Never),
717+
i32: crate::next_solver::Ty::new_int(interner, rustc_type_ir::IntTy::I32),
718+
f64: crate::next_solver::Ty::new_float(interner, rustc_type_ir::FloatTy::F64),
719+
}
720+
}
721+
}
722+
689723
/// The inference context contains all information needed during type inference.
690724
#[derive(Clone, Debug)]
691725
pub(crate) struct InferenceContext<'db> {
@@ -718,6 +752,7 @@ pub(crate) struct InferenceContext<'db> {
718752
resume_yield_tys: Option<(Ty, Ty)>,
719753
diverges: Diverges,
720754
breakables: Vec<BreakableContext<'db>>,
755+
types: InternedStandardTypesNextSolver<'db>,
721756

722757
/// Whether we are inside the pattern of a destructuring assignment.
723758
inside_assignment: bool,
@@ -798,11 +833,13 @@ impl<'db> InferenceContext<'db> {
798833
resolver: Resolver<'db>,
799834
) -> Self {
800835
let trait_env = db.trait_environment_for_body(owner);
836+
let table = unify::InferenceTable::new(db, trait_env);
801837
InferenceContext {
838+
types: InternedStandardTypesNextSolver::new(table.interner),
802839
target_features: OnceCell::new(),
803840
generics: OnceCell::new(),
804841
result: InferenceResult::default(),
805-
table: unify::InferenceTable::new(db, trait_env),
842+
table,
806843
tuple_field_accesses_rev: Default::default(),
807844
return_ty: TyKind::Error.intern(Interner), // set in collect_* calls
808845
resume_yield_tys: None,
@@ -865,24 +902,33 @@ impl<'db> InferenceContext<'db> {
865902
self.result.has_errors = true;
866903
}
867904

868-
// FIXME: This function should be private in module. It is currently only used in the consteval, since we need
869-
// `InferenceResult` in the middle of inference. See the fixme comment in `consteval::eval_to_const`. If you
870-
// used this function for another workaround, mention it here. If you really need this function and believe that
871-
// there is no problem in it being `pub(crate)`, remove this comment.
872-
pub(crate) fn resolve_all(mut self) -> InferenceResult {
873-
self.table.select_obligations_where_possible();
874-
self.table.fallback_if_possible();
905+
/// Clones `self` and calls `resolve_all()` on it.
906+
// FIXME: Remove this.
907+
pub(crate) fn fixme_resolve_all_clone(&self) -> InferenceResult {
908+
let mut ctx = self.clone();
909+
910+
ctx.type_inference_fallback();
875911

876912
// Comment from rustc:
877913
// Even though coercion casts provide type hints, we check casts after fallback for
878914
// backwards compatibility. This makes fallback a stronger type hint than a cast coercion.
879-
let cast_checks = std::mem::take(&mut self.deferred_cast_checks);
915+
let cast_checks = std::mem::take(&mut ctx.deferred_cast_checks);
880916
for mut cast in cast_checks.into_iter() {
881-
if let Err(diag) = cast.check(&mut self) {
882-
self.diagnostics.push(diag);
917+
if let Err(diag) = cast.check(&mut ctx) {
918+
ctx.diagnostics.push(diag);
883919
}
884920
}
885921

922+
ctx.table.select_obligations_where_possible();
923+
924+
ctx.resolve_all()
925+
}
926+
927+
// FIXME: This function should be private in module. It is currently only used in the consteval, since we need
928+
// `InferenceResult` in the middle of inference. See the fixme comment in `consteval::eval_to_const`. If you
929+
// used this function for another workaround, mention it here. If you really need this function and believe that
930+
// there is no problem in it being `pub(crate)`, remove this comment.
931+
pub(crate) fn resolve_all(self) -> InferenceResult {
886932
let InferenceContext {
887933
mut table, mut result, tuple_field_accesses_rev, diagnostics, ..
888934
} = self;
@@ -914,11 +960,6 @@ impl<'db> InferenceContext<'db> {
914960
diagnostics: _,
915961
} = &mut result;
916962

917-
// FIXME resolve obligations as well (use Guidance if necessary)
918-
table.select_obligations_where_possible();
919-
920-
// make sure diverging type variables are marked as such
921-
table.propagate_diverging_flag();
922963
for ty in type_of_expr.values_mut() {
923964
*ty = table.resolve_completely(ty.clone());
924965
*has_errors = *has_errors || ty.contains_unknown();
@@ -1673,6 +1714,22 @@ impl<'db> InferenceContext<'db> {
16731714
self.resolve_associated_type_with_params(inner_ty, assoc_ty, &[])
16741715
}
16751716

1717+
fn demand_eqtype(
1718+
&mut self,
1719+
expected: crate::next_solver::Ty<'db>,
1720+
actual: crate::next_solver::Ty<'db>,
1721+
) {
1722+
let result = self
1723+
.table
1724+
.infer_ctxt
1725+
.at(&ObligationCause::new(), self.table.trait_env.env)
1726+
.eq(DefineOpaqueTypes::Yes, expected, actual)
1727+
.map(|infer_ok| self.table.register_infer_ok(infer_ok));
1728+
if let Err(_err) = result {
1729+
// FIXME: Emit diagnostic.
1730+
}
1731+
}
1732+
16761733
fn resolve_associated_type_with_params(
16771734
&mut self,
16781735
inner_ty: Ty,

crates/hir-ty/src/infer/coerce.rs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -210,9 +210,8 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
210210
// Coercing from `!` to any type is allowed:
211211
if a.is_never() {
212212
// If we're coercing into an inference var, mark it as possibly diverging.
213-
// FIXME: rustc does this differently.
214-
if let TyKind::Infer(rustc_type_ir::TyVar(b)) = b.kind() {
215-
self.table.set_diverging(b.as_u32().into(), chalk_ir::TyVariableKind::General);
213+
if b.is_infer() {
214+
self.table.set_diverging(b);
216215
}
217216

218217
if self.coerce_never {
@@ -1613,16 +1612,21 @@ fn coerce<'db>(
16131612
chalk_ir::GenericArgData::Const(c) => c.inference_var(Interner),
16141613
} == Some(iv))
16151614
};
1616-
let fallback = |iv, kind, default, binder| match kind {
1617-
chalk_ir::VariableKind::Ty(_ty_kind) => find_var(iv)
1618-
.map_or(default, |i| crate::BoundVar::new(binder, i).to_ty(Interner).cast(Interner)),
1619-
chalk_ir::VariableKind::Lifetime => find_var(iv).map_or(default, |i| {
1620-
crate::BoundVar::new(binder, i).to_lifetime(Interner).cast(Interner)
1621-
}),
1622-
chalk_ir::VariableKind::Const(ty) => find_var(iv).map_or(default, |i| {
1623-
crate::BoundVar::new(binder, i).to_const(Interner, ty).cast(Interner)
1624-
}),
1615+
let fallback = |iv, kind, binder| match kind {
1616+
chalk_ir::VariableKind::Ty(_ty_kind) => find_var(iv).map_or_else(
1617+
|| chalk_ir::TyKind::Error.intern(Interner).cast(Interner),
1618+
|i| crate::BoundVar::new(binder, i).to_ty(Interner).cast(Interner),
1619+
),
1620+
chalk_ir::VariableKind::Lifetime => find_var(iv).map_or_else(
1621+
|| crate::LifetimeData::Error.intern(Interner).cast(Interner),
1622+
|i| crate::BoundVar::new(binder, i).to_lifetime(Interner).cast(Interner),
1623+
),
1624+
chalk_ir::VariableKind::Const(ty) => find_var(iv).map_or_else(
1625+
|| crate::unknown_const(ty.clone()).cast(Interner),
1626+
|i| crate::BoundVar::new(binder, i).to_const(Interner, ty.clone()).cast(Interner),
1627+
),
16251628
};
16261629
// FIXME also map the types in the adjustments
1630+
// FIXME: We don't fallback correctly since this is done on `InferenceContext` and we only have `InferenceTable`.
16271631
Ok((adjustments, table.resolve_with_fallback(ty.to_chalk(table.interner), &fallback)))
16281632
}

0 commit comments

Comments
 (0)