Repository navigation
Conversation
2c69161 to
b14b51a
Compare
03284cb to
0455baf
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Implicit argument handling and parent-scope checks still produce incorrect or incomplete nested-type inference.
2 open issues
What changed in this PR
Extends Unified’s type inference to represent nested types and preserve their enclosing generic arguments, with Swift regression coverage.
Changes:
- Adds parent-type parameters and qualified type names.
- Updates type mentions and enum construction to propagate parent-type paths.
- Adds nested-type tests and updates inference expectations.
| File | Description |
|---|---|
unified/ql/test/library-tests/type-inference/type-inference.expected |
Updates expected inference results. |
unified/ql/test/library-tests/type-inference/nested_types.swift |
Adds nested-generic, self-type, and extension coverage. |
unified/ql/test/library-tests/type-inference/generics.swift |
Removes a resolved MISSING annotation. |
unified/ql/test/library-tests/type-inference/classes.swift |
Updates the inline expected nested-type name. |
unified/ql/lib/codeql/unified/internal/typeinference/TypeMention.qll |
Adds nested-type qualification and implicit argument handling. |
unified/ql/lib/codeql/unified/internal/typeinference/TypeInferencePluginSwift.qll |
Removes Swift-specific enum plugin hooks. |
unified/ql/lib/codeql/unified/internal/typeinference/TypeInferencePlugin.qll |
Removes enum-specific hooks and wrappers. |
unified/ql/lib/codeql/unified/internal/typeinference/TypeInference.qll |
Integrates parent paths into constructor inference. |
unified/ql/lib/codeql/unified/internal/typeinference/Type.qll |
Introduces parent-type parameters and qualified names. |
unified/ql/lib/codeql/unified/internal/AstExtra.qll |
Adds shared enum relationships and enum-field modeling. |
Review effort: Max
0455baf to
e55b569
Compare
606fac6 to
fee52f9
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Parent-type inference can recurse into itself and produce conflicting types for nested classes and aliases.
7 open issues
Prefer lexical parent over enclosing subclass fallback · New Preserve alias-expanded parent specialization · New Exclude fixed-specialization aliases from implicit arguments · New Exclude generic alias bases from parent fallback · New Avoid fallback on specialized unqualified alias uses · New Correct expected and spurious constructor targets · New Use bar(...) for zero-argument type inference · New
2 resolved since last review
Review effort: Max
fee52f9 to
a014cb6
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Alias expansion can lose enclosing generic arguments or introduce conflicting certain types.
3 open issues
7 resolved since last review
Prefer lexical parent over enclosing subclass fallback Use bar(...) for zero-argument type inference Correct expected and spurious constructor targets Avoid fallback on specialized unqualified alias uses Exclude generic alias bases from parent fallback Exclude fixed-specialization aliases from implicit arguments Preserve alias-expanded parent specialization
Review effort: Max
9a2da2f to
f27b5df
Compare
5ffdc62 to
16e479a
Compare
16e479a to
d259816
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Nested alias handling still loses qualifier information in forwarded references, generic arguments, and tuple elements, causing incorrect outer-type inference for qualified alias uses.
2 open issues
1 resolved since last review
Review effort: Max
af6d3b3 to
cd773cb
Compare
There was a problem hiding this comment.
🟡 Changes recommended
The enum-field inference rule misses generic arguments beneath parent paths, preventing nested enum initializers from acquiring outer type arguments from their annotations.
1 open issue
2 resolved since last review
Review effort: Max
12f09e4 to
6475b06
Compare
6475b06 to
6ca07df
Compare
| private import codeql.unified.internal.ExprPositions | ||
|
|
||
| /** | ||
| * Holds if `alias` is an alias at `pathToNested` for nested type `nested` with |
There was a problem hiding this comment.
The docs here are not that easy to follow. What does it means to be an alias "at" a given type path? Would it make sense to say that it contains an alias for nested at pathToNested?
Independently of that, I'd suggest adding type parameters on the Parent class in the example. At least for me it was easier to see the "why" behind this after seeing the example for getExpandedTypeAt where there are type parameters in play.
Lastly, I would have expected this logic to build on UnqualifiedMemberAccess and its getAccessingClass predicate, in order to treat unqualified type mentions in general as it they were qualified. For example, Nested in Parent<T> would be seen as Parent<T>.Nested. I'm not sure why aliases are special-cased to this degree, since unqualified type mentions can appear in many other places. Why is all this not just an extension of getTypeAt using UnqualifiedMemberAccess.getAccessingClass to populate chains of ParentTypeParameters leading to the accessing class?
There was a problem hiding this comment.
The docs here are not that easy to follow. What does it means to be an alias "at" a given type path? Would it make sense to say that it contains an alias for
nestedatpathToNested?
I will try to make it clearer.
Independently of that, I'd suggest adding type parameters on the
Parentclass in the example. At least for me it was easier to see the "why" behind this after seeing the example forgetExpandedTypeAtwhere there are type parameters in play.
Will do.
Lastly, I would have expected this logic to build on
UnqualifiedMemberAccessand itsgetAccessingClasspredicate, in order to treat unqualified type mentions in general as it they were qualified. For example,NestedinParent<T>would be seen asParent<T>.Nested. I'm not sure why aliases are special-cased to this degree, since unqualified type mentions can appear in many other places. Why is all this not just an extension ofgetTypeAtusingUnqualifiedMemberAccess.getAccessingClassto populate chains ofParentTypeParameters leading to the accessing class?
I should perhaps use different terminology than qualified then: For example, if we add
typealias Unbound = Holder.InnerAliasto the class Outer in the test example, then the RHS is qualified, but it doesn't tell us the parent type (in contrast, the existing Fixed alias already present in the test does pin the parent type). I'll add this test example.
There was a problem hiding this comment.
I have pushed an update. In the end I went with "orphaned" instead of "unqualified".
| parent = nested.getEnclosingClassResolved() and | ||
| alias.getEnclosingClassResolved+() = parent and | ||
| pathParents = TypePath::singleton(TParentTypeParameterType(alias)) and | ||
| encl = alias.getEnclosingClassResolved() |
There was a problem hiding this comment.
What happens when nested comes from a base class? Then the alias and its target don't have a common enclosing class.
class Base<T> {
struct Nested { var x: T }
}
class Sub : Base<String> {
typealias Element = Nested
}
let x: Sub.Element // --> Base<String>.NestedThere was a problem hiding this comment.
Good catch. Though, instead of resolving Sub.Element to Base<String>.Nested it should resolve to Sub.Nested (and it would then be the job of sub typing to be able to match T in Base with String, which requires variance support).
| this.getSimpleExpandedTypeAt(prefix | ||
| .appendInverse(any(TypePath cons | cons.isCons(tp, suffix)))) and |
There was a problem hiding this comment.
This is very hard to follow. Could we rewrite this to put the recursive call first with a variable as its argument, and bind prefix based on this?



This PR adds support for nested types in type inference. Since nested types can interact with both generics and sub typing, we need a way to represent outer types in addition to the nested type itself:
For example, we need to distinguish
Outer<Int>.Inner<Int>fromOuter<Bool>.Inner<Int>, as well as fromSubOuter<Int>.Inner<Int>.We do this by adding a pseudo type parameter to each nested type, which will hold type information about the outer type. For example,
Outer<Int>.Inner<String>is represented asTypePathType""Inner"TInner"String"Inner_Parent"Outer"Inner_Parent.TOuter"Intwhere
Inner_Parentis the special type parameter belonging to the nested classInner.Things can get further complicated when type aliases are used, which prompted a change to the existing
TypeMentionlogic: Instead of resolving aliases directly in thegetTypeAtrelation, we split resolution logic into different phases, where the initialgetUnexpandedTypeAtdoes not expand aliases, but instead treat them as ordinary types. Ultimately, ingetTypeAt, aliases are fully expanded, and type inference still does not need to worry about them.DCA is fine.