Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit fc92ee8

Browse files
committed
Auto merge of rust-lang#17541 - ShoyuVanilla:nested-impl-traits, r=Veykril
Disallow nested impl traits Fixes rust-lang#17498 The above issue is due to formatting self referencing, recursive bound like `Implemented(^0.0: TraitId(0)<[?0 := ^0.0]>)` on the codes like; ```rust trait Foo<T> {} trait Bar {} fn test(foo: impl Foo<impl Bar>) { ... } ``` When lowering predicate `impl Foo<impl Bar>` in `trait_environment_query`, the outer `impl Foo<...>` is treated as predicates, so the first `TypeRef` that passes the following code is `impl Bar`; https://github.com/rust-lang/rust-analyzer/blob/cae997e3380363a906588f14c7b4587f39cf09f5/crates/hir-ty/src/lower.rs#L376-L400 and thus the `idx` is `0` in the above context. But the following code sets `self_ty` as the `BoundVar` with `idx = 0` because the target param id for predicate `impl Foo<...>` is `0` since `impl Foo` is the first generic-like parameter of `fn test`; https://github.com/rust-lang/rust-analyzer/blob/cae997e3380363a906588f14c7b4587f39cf09f5/crates/hir-ty/src/lower.rs#L998-L1025 For the codes like; ```rust trait Foo { type Assoc; } trait Bar {} fn test(foo: impl Foo<Assoc = impl Bar>) { ... } ``` similar recursive bound doesn't happen because the following codes ***"count the number of `impl Trait` things that appear before the target of our `bound`."*** https://github.com/rust-lang/rust-analyzer/blob/cae997e3380363a906588f14c7b4587f39cf09f5/crates/hir-ty/src/lower.rs#L1168-L1199 Instead of doing similar thing like nested `impl Foo<impl Bar>` thing, this PR lowers such nested impl traits into error types in the similar manner as the rustc does in the following lines (and of course, allows lowering and inferencing nested impl traits for the cases that rustc permits); - https://github.com/rust-lang/rust/blob/e2cf31a6148725bde4ea48acf1e4fe72675257a2/compiler/rustc_ast_passes/src/ast_validation.rs#L802-L813 - https://github.com/rust-lang/rust/blob/7b21c18fe4de32a7d2faa468e6ef69abff013f85/compiler/rustc_ast_passes/src/ast_validation.rs#L1299-L1314 (Though rustc emits [E0666😈](https://doc.rust-lang.org/error_codes/E0666.html), I skipped diagnostics since gathering diagnostics in `hir-def` has no conventions so far 😅)
2 parents 58bf593 + 90814f4 commit fc92ee8

File tree

4 files changed

+112
-1
lines changed

4 files changed

+112
-1
lines changed

src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,13 @@ impl TypeRef {
245245
// for types are close enough for our purposes to the inner type for now...
246246
ast::Type::ForType(inner) => TypeRef::from_ast_opt(ctx, inner.ty()),
247247
ast::Type::ImplTraitType(inner) => {
248-
TypeRef::ImplTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
248+
if ctx.outer_impl_trait() {
249+
// Disallow nested impl traits
250+
TypeRef::Error
251+
} else {
252+
let _guard = ctx.outer_impl_trait_scope(true);
253+
TypeRef::ImplTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
254+
}
249255
}
250256
ast::Type::DynTraitType(inner) => {
251257
TypeRef::DynTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))

src/tools/rust-analyzer/crates/hir-def/src/lower.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,26 @@ pub struct LowerCtx<'a> {
1818
span_map: OnceCell<SpanMap>,
1919
ast_id_map: OnceCell<Arc<AstIdMap>>,
2020
impl_trait_bounds: RefCell<Vec<Vec<Interned<TypeBound>>>>,
21+
// Prevent nested impl traits like `impl Foo<impl Bar>`.
22+
outer_impl_trait: RefCell<bool>,
23+
}
24+
25+
pub(crate) struct OuterImplTraitGuard<'a> {
26+
ctx: &'a LowerCtx<'a>,
27+
old: bool,
28+
}
29+
30+
impl<'a> OuterImplTraitGuard<'a> {
31+
fn new(ctx: &'a LowerCtx<'a>, impl_trait: bool) -> Self {
32+
let old = ctx.outer_impl_trait.replace(impl_trait);
33+
Self { ctx, old }
34+
}
35+
}
36+
37+
impl<'a> Drop for OuterImplTraitGuard<'a> {
38+
fn drop(&mut self) {
39+
self.ctx.outer_impl_trait.replace(self.old);
40+
}
2141
}
2242

2343
impl<'a> LowerCtx<'a> {
@@ -28,6 +48,7 @@ impl<'a> LowerCtx<'a> {
2848
span_map: OnceCell::new(),
2949
ast_id_map: OnceCell::new(),
3050
impl_trait_bounds: RefCell::new(Vec::new()),
51+
outer_impl_trait: RefCell::default(),
3152
}
3253
}
3354

@@ -42,6 +63,7 @@ impl<'a> LowerCtx<'a> {
4263
span_map,
4364
ast_id_map: OnceCell::new(),
4465
impl_trait_bounds: RefCell::new(Vec::new()),
66+
outer_impl_trait: RefCell::default(),
4567
}
4668
}
4769

@@ -67,4 +89,12 @@ impl<'a> LowerCtx<'a> {
6789
pub fn take_impl_traits_bounds(&self) -> Vec<Vec<Interned<TypeBound>>> {
6890
self.impl_trait_bounds.take()
6991
}
92+
93+
pub(crate) fn outer_impl_trait(&self) -> bool {
94+
*self.outer_impl_trait.borrow()
95+
}
96+
97+
pub(crate) fn outer_impl_trait_scope(&'a self, impl_trait: bool) -> OuterImplTraitGuard<'a> {
98+
OuterImplTraitGuard::new(self, impl_trait)
99+
}
70100
}

src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,8 @@ pub(super) fn lower_generic_args(
202202
continue;
203203
}
204204
if let Some(name_ref) = assoc_type_arg.name_ref() {
205+
// Nested impl traits like `impl Foo<Assoc = impl Bar>` are allowed
206+
let _guard = lower_ctx.outer_impl_trait_scope(false);
205207
let name = name_ref.as_name();
206208
let args = assoc_type_arg
207209
.generic_arg_list()

src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4824,3 +4824,76 @@ fn foo() {
48244824
"#,
48254825
)
48264826
}
4827+
4828+
#[test]
4829+
fn nested_impl_traits() {
4830+
check_infer(
4831+
r#"
4832+
//- minicore: fn
4833+
trait Foo {}
4834+
4835+
trait Bar<T> {}
4836+
4837+
trait Baz {
4838+
type Assoc;
4839+
}
4840+
4841+
struct Qux<T> {
4842+
qux: T,
4843+
}
4844+
4845+
struct S;
4846+
4847+
impl Foo for S {}
4848+
4849+
fn not_allowed1(f: impl Fn(impl Foo)) {
4850+
let foo = S;
4851+
f(foo);
4852+
}
4853+
4854+
// This caused stack overflow in #17498
4855+
fn not_allowed2(f: impl Fn(&impl Foo)) {
4856+
let foo = S;
4857+
f(&foo);
4858+
}
4859+
4860+
fn not_allowed3(bar: impl Bar<impl Foo>) {}
4861+
4862+
// This also caused stack overflow
4863+
fn not_allowed4(bar: impl Bar<&impl Foo>) {}
4864+
4865+
fn allowed1(baz: impl Baz<Assoc = impl Foo>) {}
4866+
4867+
fn allowed2<'a>(baz: impl Baz<Assoc = &'a (impl Foo + 'a)>) {}
4868+
4869+
fn allowed3(baz: impl Baz<Assoc = Qux<impl Foo>>) {}
4870+
"#,
4871+
expect![[r#"
4872+
139..140 'f': impl Fn({unknown}) + ?Sized
4873+
161..193 '{ ...oo); }': ()
4874+
171..174 'foo': S
4875+
177..178 'S': S
4876+
184..185 'f': impl Fn({unknown}) + ?Sized
4877+
184..190 'f(foo)': ()
4878+
186..189 'foo': S
4879+
251..252 'f': impl Fn(&'? {unknown}) + ?Sized
4880+
274..307 '{ ...oo); }': ()
4881+
284..287 'foo': S
4882+
290..291 'S': S
4883+
297..298 'f': impl Fn(&'? {unknown}) + ?Sized
4884+
297..304 'f(&foo)': ()
4885+
299..303 '&foo': &'? S
4886+
300..303 'foo': S
4887+
325..328 'bar': impl Bar<{unknown}> + ?Sized
4888+
350..352 '{}': ()
4889+
405..408 'bar': impl Bar<&'? {unknown}> + ?Sized
4890+
431..433 '{}': ()
4891+
447..450 'baz': impl Baz<Assoc = impl Foo + ?Sized> + ?Sized
4892+
480..482 '{}': ()
4893+
500..503 'baz': impl Baz<Assoc = &'a impl Foo + 'a + ?Sized> + ?Sized
4894+
544..546 '{}': ()
4895+
560..563 'baz': impl Baz<Assoc = Qux<impl Foo + ?Sized>> + ?Sized
4896+
598..600 '{}': ()
4897+
"#]],
4898+
)
4899+
}

0 commit comments

Comments
 (0)