Skip to content

Commit 2e7059c

Browse files
committed
Auto merge of #16877 - Veykril:stackoverflow, r=Veykril
fix: Fix `impl Trait<Self>` causing stackoverflows Fixes #15646
2 parents 40bb8f3 + 1915980 commit 2e7059c

File tree

11 files changed

+228
-144
lines changed

11 files changed

+228
-144
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ smol_str.opt-level = 3
2222
text-size.opt-level = 3
2323
# This speeds up `cargo xtask dist`.
2424
miniz_oxide.opt-level = 3
25+
salsa.opt-level = 3
2526

2627
[profile.release]
2728
incremental = true

crates/hir-ty/src/display.rs

+118-107
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use intern::{Internable, Interned};
2828
use itertools::Itertools;
2929
use la_arena::ArenaMap;
3030
use smallvec::SmallVec;
31-
use stdx::never;
31+
use stdx::{never, IsNoneOr};
3232
use triomphe::Arc;
3333

3434
use crate::{
@@ -41,9 +41,9 @@ use crate::{
4141
mir::pad16,
4242
primitive, to_assoc_type_id,
4343
utils::{self, detect_variant_from_bytes, generics, ClosureSubst},
44-
AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstScalar, ConstValue,
45-
DomainGoal, FnAbi, GenericArg, GenericArgData, ImplTraitId, Interner, Lifetime, LifetimeData,
46-
LifetimeOutlives, MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt,
44+
AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, ConcreteConst, Const,
45+
ConstScalar, ConstValue, DomainGoal, FnAbi, GenericArg, ImplTraitId, Interner, Lifetime,
46+
LifetimeData, LifetimeOutlives, MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt,
4747
QuantifiedWhereClause, Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty,
4848
TyExt, WhereClause,
4949
};
@@ -60,11 +60,18 @@ impl HirWrite for String {}
6060
impl HirWrite for fmt::Formatter<'_> {}
6161

6262
pub struct HirFormatter<'a> {
63+
/// The database handle
6364
pub db: &'a dyn HirDatabase,
65+
/// The sink to write into
6466
fmt: &'a mut dyn HirWrite,
67+
/// A buffer to intercept writes with, this allows us to track the overall size of the formatted output.
6568
buf: String,
69+
/// The current size of the formatted output.
6670
curr_size: usize,
67-
pub(crate) max_size: Option<usize>,
71+
/// Size from which we should truncate the output.
72+
max_size: Option<usize>,
73+
/// When rendering something that has a concept of "children" (like fields in a struct), this limits
74+
/// how many should be rendered.
6875
pub entity_limit: Option<usize>,
6976
omit_verbose_types: bool,
7077
closure_style: ClosureStyle,
@@ -304,7 +311,6 @@ impl DisplayTarget {
304311
#[derive(Debug)]
305312
pub enum DisplaySourceCodeError {
306313
PathNotFound,
307-
UnknownType,
308314
Coroutine,
309315
OpaqueType,
310316
}
@@ -418,6 +424,7 @@ impl HirDisplay for ProjectionTy {
418424
let proj_params = &self.substitution.as_slice(Interner)[..proj_params_count];
419425
if !proj_params.is_empty() {
420426
write!(f, "<")?;
427+
// FIXME use `hir_fmt_generics` here
421428
f.write_joined(proj_params, ", ")?;
422429
write!(f, ">")?;
423430
}
@@ -967,6 +974,7 @@ impl HirDisplay for Ty {
967974
.chain(fn_params)
968975
.flatten();
969976
write!(f, "<")?;
977+
// FIXME use `hir_fmt_generics` here
970978
f.write_joined(params, ", ")?;
971979
write!(f, ">")?;
972980
}
@@ -1037,6 +1045,7 @@ impl HirDisplay for Ty {
10371045
// FIXME: reconsider the generic args order upon formatting?
10381046
if parameters.len(Interner) > 0 {
10391047
write!(f, "<")?;
1048+
// FIXME use `hir_fmt_generics` here
10401049
f.write_joined(parameters.as_slice(Interner), ", ")?;
10411050
write!(f, ">")?;
10421051
}
@@ -1287,11 +1296,10 @@ impl HirDisplay for Ty {
12871296
}
12881297
TyKind::Error => {
12891298
if f.display_target.is_source_code() {
1290-
return Err(HirDisplayError::DisplaySourceCodeError(
1291-
DisplaySourceCodeError::UnknownType,
1292-
));
1299+
f.write_char('_')?;
1300+
} else {
1301+
write!(f, "{{unknown}}")?;
12931302
}
1294-
write!(f, "{{unknown}}")?;
12951303
}
12961304
TyKind::InferenceVar(..) => write!(f, "_")?,
12971305
TyKind::Coroutine(_, subst) => {
@@ -1331,98 +1339,90 @@ fn hir_fmt_generics(
13311339
parameters: &Substitution,
13321340
generic_def: Option<hir_def::GenericDefId>,
13331341
) -> Result<(), HirDisplayError> {
1334-
let db = f.db;
1335-
if parameters.len(Interner) > 0 {
1336-
use std::cmp::Ordering;
1337-
let param_compare =
1338-
|a: &GenericArg, b: &GenericArg| match (a.data(Interner), b.data(Interner)) {
1339-
(crate::GenericArgData::Lifetime(_), crate::GenericArgData::Lifetime(_)) => {
1340-
Ordering::Equal
1341-
}
1342-
(crate::GenericArgData::Lifetime(_), _) => Ordering::Less,
1343-
(_, crate::GenericArgData::Lifetime(_)) => Ordering::Less,
1344-
(_, _) => Ordering::Equal,
1345-
};
1346-
let parameters_to_write = if f.display_target.is_source_code() || f.omit_verbose_types() {
1347-
match generic_def
1348-
.map(|generic_def_id| db.generic_defaults(generic_def_id))
1349-
.filter(|defaults| !defaults.is_empty())
1350-
{
1351-
None => parameters.as_slice(Interner),
1352-
Some(default_parameters) => {
1353-
fn should_show(
1354-
parameter: &GenericArg,
1355-
default_parameters: &[Binders<GenericArg>],
1356-
i: usize,
1357-
parameters: &Substitution,
1358-
) -> bool {
1359-
if parameter.ty(Interner).map(|it| it.kind(Interner))
1360-
== Some(&TyKind::Error)
1361-
{
1362-
return true;
1363-
}
1364-
if let Some(ConstValue::Concrete(c)) =
1365-
parameter.constant(Interner).map(|it| &it.data(Interner).value)
1366-
{
1367-
if c.interned == ConstScalar::Unknown {
1368-
return true;
1369-
}
1370-
}
1371-
if let Some(crate::LifetimeData::Static | crate::LifetimeData::Error) =
1372-
parameter.lifetime(Interner).map(|it| it.data(Interner))
1373-
{
1374-
return true;
1342+
if parameters.is_empty(Interner) {
1343+
return Ok(());
1344+
}
1345+
1346+
let parameters_to_write =
1347+
generic_args_sans_defaults(f, generic_def, parameters.as_slice(Interner));
1348+
if !parameters_to_write.is_empty() {
1349+
write!(f, "<")?;
1350+
hir_fmt_generic_arguments(f, parameters_to_write)?;
1351+
write!(f, ">")?;
1352+
}
1353+
1354+
Ok(())
1355+
}
1356+
1357+
fn generic_args_sans_defaults<'ga>(
1358+
f: &mut HirFormatter<'_>,
1359+
generic_def: Option<hir_def::GenericDefId>,
1360+
parameters: &'ga [GenericArg],
1361+
) -> &'ga [GenericArg] {
1362+
if f.display_target.is_source_code() || f.omit_verbose_types() {
1363+
match generic_def
1364+
.map(|generic_def_id| f.db.generic_defaults(generic_def_id))
1365+
.filter(|it| !it.is_empty())
1366+
{
1367+
None => parameters,
1368+
Some(default_parameters) => {
1369+
let should_show = |arg: &GenericArg, i: usize| {
1370+
let is_err = |arg: &GenericArg| match arg.data(Interner) {
1371+
chalk_ir::GenericArgData::Lifetime(it) => {
1372+
*it.data(Interner) == LifetimeData::Error
13751373
}
1376-
let default_parameter = match default_parameters.get(i) {
1377-
Some(it) => it,
1378-
None => return true,
1379-
};
1380-
let actual_default =
1381-
default_parameter.clone().substitute(Interner, &parameters);
1382-
parameter != &actual_default
1374+
chalk_ir::GenericArgData::Ty(it) => *it.kind(Interner) == TyKind::Error,
1375+
chalk_ir::GenericArgData::Const(it) => matches!(
1376+
it.data(Interner).value,
1377+
ConstValue::Concrete(ConcreteConst {
1378+
interned: ConstScalar::Unknown,
1379+
..
1380+
})
1381+
),
1382+
};
1383+
// if the arg is error like, render it to inform the user
1384+
if is_err(arg) {
1385+
return true;
13831386
}
1384-
let mut default_from = 0;
1385-
for (i, parameter) in parameters.iter(Interner).enumerate() {
1386-
if should_show(parameter, &default_parameters, i, parameters) {
1387-
default_from = i + 1;
1388-
}
1387+
// otherwise, if the arg is equal to the param default, hide it (unless the
1388+
// default is an error which can happen for the trait Self type)
1389+
default_parameters.get(i).is_none_or(|default_parameter| {
1390+
// !is_err(default_parameter.skip_binders())
1391+
// &&
1392+
arg != &default_parameter.clone().substitute(Interner, &parameters)
1393+
})
1394+
};
1395+
let mut default_from = 0;
1396+
for (i, parameter) in parameters.iter().enumerate() {
1397+
if should_show(parameter, i) {
1398+
default_from = i + 1;
13891399
}
1390-
&parameters.as_slice(Interner)[0..default_from]
1391-
}
1392-
}
1393-
} else {
1394-
parameters.as_slice(Interner)
1395-
};
1396-
//FIXME: Should handle the ordering of lifetimes when creating substitutions
1397-
let mut parameters_to_write = parameters_to_write.to_vec();
1398-
parameters_to_write.sort_by(param_compare);
1399-
if !parameters_to_write.is_empty() {
1400-
write!(f, "<")?;
1401-
let mut first = true;
1402-
for generic_arg in parameters_to_write {
1403-
if !first {
1404-
write!(f, ", ")?;
1405-
}
1406-
first = false;
1407-
if f.display_target.is_source_code() {
1408-
match generic_arg.data(Interner) {
1409-
GenericArgData::Lifetime(l)
1410-
if matches!(l.data(Interner), LifetimeData::Error) =>
1411-
{
1412-
write!(f, "'_")
1413-
}
1414-
GenericArgData::Ty(t) if matches!(t.kind(Interner), TyKind::Error) => {
1415-
write!(f, "_")
1416-
}
1417-
_ => generic_arg.hir_fmt(f),
1418-
}?
1419-
} else {
1420-
generic_arg.hir_fmt(f)?;
14211400
}
1401+
&parameters[0..default_from]
14221402
}
1403+
}
1404+
} else {
1405+
parameters
1406+
}
1407+
}
14231408

1424-
write!(f, ">")?;
1409+
fn hir_fmt_generic_arguments(
1410+
f: &mut HirFormatter<'_>,
1411+
parameters: &[GenericArg],
1412+
) -> Result<(), HirDisplayError> {
1413+
let mut first = true;
1414+
let lifetime_offset = parameters.iter().position(|arg| arg.lifetime(Interner).is_some());
1415+
1416+
let (ty_or_const, lifetimes) = match lifetime_offset {
1417+
Some(offset) => parameters.split_at(offset),
1418+
None => (parameters, &[][..]),
1419+
};
1420+
for generic_arg in lifetimes.iter().chain(ty_or_const) {
1421+
if !first {
1422+
write!(f, ", ")?;
14251423
}
1424+
first = false;
1425+
generic_arg.hir_fmt(f)?;
14261426
}
14271427
Ok(())
14281428
}
@@ -1544,20 +1544,29 @@ fn write_bounds_like_dyn_trait(
15441544
f.start_location_link(trait_.into());
15451545
write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast()))?;
15461546
f.end_location_link();
1547-
if let [_, params @ ..] = trait_ref.substitution.as_slice(Interner) {
1548-
if is_fn_trait {
1547+
if is_fn_trait {
1548+
if let [_self, params @ ..] = trait_ref.substitution.as_slice(Interner) {
15491549
if let Some(args) =
15501550
params.first().and_then(|it| it.assert_ty_ref(Interner).as_tuple())
15511551
{
15521552
write!(f, "(")?;
1553-
f.write_joined(args.as_slice(Interner), ", ")?;
1553+
hir_fmt_generic_arguments(f, args.as_slice(Interner))?;
15541554
write!(f, ")")?;
15551555
}
1556-
} else if !params.is_empty() {
1557-
write!(f, "<")?;
1558-
f.write_joined(params, ", ")?;
1559-
// there might be assoc type bindings, so we leave the angle brackets open
1560-
angle_open = true;
1556+
}
1557+
} else {
1558+
let params = generic_args_sans_defaults(
1559+
f,
1560+
Some(trait_.into()),
1561+
trait_ref.substitution.as_slice(Interner),
1562+
);
1563+
if let [_self, params @ ..] = params {
1564+
if !params.is_empty() {
1565+
write!(f, "<")?;
1566+
hir_fmt_generic_arguments(f, params)?;
1567+
// there might be assoc type bindings, so we leave the angle brackets open
1568+
angle_open = true;
1569+
}
15611570
}
15621571
}
15631572
}
@@ -1609,9 +1618,9 @@ fn write_bounds_like_dyn_trait(
16091618
let proj_arg_count = generics(f.db.upcast(), assoc_ty_id.into()).len_self();
16101619
if proj_arg_count > 0 {
16111620
write!(f, "<")?;
1612-
f.write_joined(
1621+
hir_fmt_generic_arguments(
1622+
f,
16131623
&proj.substitution.as_slice(Interner)[..proj_arg_count],
1614-
", ",
16151624
)?;
16161625
write!(f, ">")?;
16171626
}
@@ -1670,6 +1679,7 @@ fn fmt_trait_ref(
16701679
f.end_location_link();
16711680
if tr.substitution.len(Interner) > 1 {
16721681
write!(f, "<")?;
1682+
// FIXME use `hir_fmt_generics` here
16731683
f.write_joined(&tr.substitution.as_slice(Interner)[1..], ", ")?;
16741684
write!(f, ">")?;
16751685
}
@@ -1728,15 +1738,16 @@ impl HirDisplay for Lifetime {
17281738
impl HirDisplay for LifetimeData {
17291739
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
17301740
match self {
1731-
LifetimeData::BoundVar(idx) => idx.hir_fmt(f),
1732-
LifetimeData::InferenceVar(_) => write!(f, "_"),
17331741
LifetimeData::Placeholder(idx) => {
17341742
let id = lt_from_placeholder_idx(f.db, *idx);
17351743
let generics = generics(f.db.upcast(), id.parent);
17361744
let param_data = &generics.params[id.local_id];
17371745
write!(f, "{}", param_data.name.display(f.db.upcast()))?;
17381746
Ok(())
17391747
}
1748+
_ if f.display_target.is_source_code() => write!(f, "'_"),
1749+
LifetimeData::BoundVar(idx) => idx.hir_fmt(f),
1750+
LifetimeData::InferenceVar(_) => write!(f, "_"),
17401751
LifetimeData::Static => write!(f, "'static"),
17411752
LifetimeData::Error => write!(f, "'{{error}}"),
17421753
LifetimeData::Erased => Ok(()),

crates/hir-ty/src/lib.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ use base_db::salsa::impl_intern_value_trivial;
5656
use chalk_ir::{
5757
fold::{Shift, TypeFoldable},
5858
interner::HasInterner,
59-
visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
6059
NoSolution,
6160
};
6261
use either::Either;
@@ -98,7 +97,9 @@ pub use traits::TraitEnvironment;
9897
pub use utils::{all_super_traits, is_fn_unsafe_to_call};
9998

10099
pub use chalk_ir::{
101-
cast::Cast, AdtId, BoundVar, DebruijnIndex, Mutability, Safety, Scalar, TyVariableKind,
100+
cast::Cast,
101+
visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
102+
AdtId, BoundVar, DebruijnIndex, Mutability, Safety, Scalar, TyVariableKind,
102103
};
103104

104105
pub type ForeignDefId = chalk_ir::ForeignDefId<Interner>;

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ fn render_dyn_for_ty() {
8585
trait Foo<'a> {}
8686
8787
fn foo(foo: &dyn for<'a> Foo<'a>) {}
88-
// ^^^ &dyn Foo<'{error}>
88+
// ^^^ &dyn Foo<'_>
8989
"#,
9090
);
9191
}

crates/hir/src/lib.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -4711,10 +4711,12 @@ impl Type {
47114711
if let WhereClause::Implemented(trait_ref) = pred.skip_binders() {
47124712
cb(type_.clone());
47134713
// skip the self type. it's likely the type we just got the bounds from
4714-
for ty in
4715-
trait_ref.substitution.iter(Interner).skip(1).filter_map(|a| a.ty(Interner))
4716-
{
4717-
walk_type(db, &type_.derived(ty.clone()), cb);
4714+
if let [self_ty, params @ ..] = trait_ref.substitution.as_slice(Interner) {
4715+
for ty in
4716+
params.iter().filter(|&ty| ty != self_ty).filter_map(|a| a.ty(Interner))
4717+
{
4718+
walk_type(db, &type_.derived(ty.clone()), cb);
4719+
}
47184720
}
47194721
}
47204722
}

0 commit comments

Comments
 (0)