Skip to content

Commit 90814f4

Browse files
committed
Disallow nested impl traits
1 parent 58bf593 commit 90814f4

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)