GSoC 2026: Questions about Qualified Name Lookup Project

Hi, my name is Filip. I’m a junior majoring in Computer Science and Mathematics at Vassar College. I’m interested in the Qualified Name Lookup proposal for GSoC 2026, and with this post, I’m looking for some clarifications on the project scope. But first, an introduction. Although most of my experience has been in higher-level software development, such as apps and image-analysis plugins, I’ve recently dabbled with lower-level projects, including Embedded SwiftWasm. I’ve been passionate about Swift, actively reading and sometimes participating in forum discussions for six years now. I’ve coauthored two Swift Evolution proposals, and now I want to contribute directly to the compiler frontend.

With the introduction out of the way, I’ll now present my understanding of the project, some technical questions, and discuss next steps.

My Understanding

The project involves creating a new AST-level API for qualified name lookups in the SwiftLexicalLookup swift-syntax module. This API will accept a nominal type declaration/extension/module, and the actual name to look up. Then, it will return the member declarations found in the module, type, extensions, supertype, or conformed protocols. Namely, we can perform 4 types of lookup:

  • Regular Lookup: Given a nominal type or extension, we recursively search conformed protocols and supertypes for the given name.
  • Module Lookup: We look for matching top-level declarations in the provided module declaration.
  • Operator Lookup: We look up operator functions that match the given operator name.
  • Dynamic Lookup: Using a global lookup table, we find a member of an AnyObject type.

In other words, the project involves rewriting the NameLookupRequests.h interfaces, and porting their implementations ([-/Module/Operator]NameLookup.cpp) to Swift. Operators, in particular, will likely leverage the existing SwiftOperators module. Finally, we would borrow many of the existing name-lookup tests, with a greater focus on the AST lookup itself rather than the semantic-analysis process.

Is my understanding correct? Did I omit a major aspect of the project?

Questions

Next up, I have some specific questions:

  1. Given last year’s post for this proposal, this project does not include lookup on associated types, generic parameters, and types in where clauses, right? Reading the Generics documentation, it appears that handling those would be a semantic-analysis-level API.
  2. The SwiftIfConfig module seems to provide a lot of infrastructure for handling pound-ifs (#if), so should I expect handling pound-ifs (#if) to be easy to implement?
  3. How should we handle Delayed Parsing? Will the new API integrate with the request evaluator, or will we have a LookupResult.lookForMembers-esque API similar to unqualified lookup?
  4. Is my understanding correct that this new library will require a lot more AST-specific testing? It appears that the current tests don’t cleanly separate the AST lookup facilities and the Sema code that invokes them.
  5. Is the proposed API expected to handle C/C++ interop in some special way, or can we focus exclusively on Swift declarations?
  6. How much should I expect attached macros to complicate qualified lookup? If the name we’re looking for is in the names field of the attached attribute, then I don’t anticipate needing any special handling. However, if a macro has names: arbitrary, do we lazily prompt the request evaluator to expand the given macro? According to my understanding, the main challenge is that we need to type-check macro parameters before expanding a macro; however, Sema handles type-checking, not the parser.

Please let me know if any of my questions were unclear or if I should expand on any of my points.

Next Steps

I’m pinging @xedin, who is listed as the potential mentor. The applications open on March 16, and I’d be happy to work on drafting and refining a proposal in the meantime. I’ll also try to contribute to swift-syntax to familiarize myself with the codebase and ensure my coding abilities are a good match. I look forward to your responses, and I’m always happy to elaborate if I missed anything!

1 Like

Of these, I think the first two are probably the most important, and are possibly the same API and implementation: it's doing a lookup into either a nominal type or module for a given name.

Generally, yes. Like with SwiftLexicalLookup, we want to build APIs that are easy to use by just throwing swift-syntax trees at it (for clients that want to do simple syntactic analyses), but flexible enough that we can wire up the compiler to use the same implementation. Actually hooking into the compiler is a big task that might not fit within the GSoC time frame, though.

The SwiftOperators library is good at sequence folding, but it is designed with a "flat" namespace of operators that really isn't correct, so it isn't likely to be helpful here. Rather, I expect that once there is a qualified name lookup library, we can teach SwiftOperators to use it (or its name-within-a-module abstraction) to deal with modules correctly.

Some of this requires semantic analysis that isn't in scope for this project, yes.

Yes. If you end up building a visitor to (e.g.) walk the syntax tree to find things to add into your symbol table, then switching over to ActiveSyntaxAnyVisitor is straightforward.

We don't want the API to integrate with the request evaluator directly; that's something only the compiler will want to do. I expect we'll have an API like lookupForMembers.

My hope is that we can build a testing harness that doesn't involve the compiler at all, so it's easy to write tests within the swift-syntax repository. The flow we ended up with SwiftLexicalLookup is that it's straightforward to write a new test with annotated Swift code and check that name lookup produces the right results. When we later wired up the compiler to cross-check its own results against those of SwiftLexicalLookup, we used that to find the differences and turn them into swift-syntax tests.

Focus exclusively on Swift declarations, and rely on things like lookupForMembers as hooks for the compiler or other tools to add their own lookup results from other sources (Clang importer, on-disk modules, etc.)

My hope would be that we can deal with this via something like lookupForMembers so the library doesn't need to deal with macros.

Doug

1 Like

Thanks for the detailed response! I've written up a rough draft based on your clarifications and @codafi's post. I've pasted the draft below, but I have a few more questions before we jump in:

  1. In the proposal below I added a SymbolTable type for caching as suggested in the project description. However, Robert's response suggests we might be able to maintain a narrower scope without one. Would you recommend removing SymbolTable from the proposal?
  2. Based on Robert's post, should access control be considered explicitly out of scope for this project?
  3. I'm unsure how module lookup fits into this more narrowly scoped vision. If the API operates on a single SourceFileSyntax, how can we perform cross-module lookup?
  4. I understand that correctness and documentation are both crucial for this project. Beyond choosing efficient algorithms and avoiding excessive copying, is performance optimization a primary objective?
  5. I'm not entirely sure how to handle implicit conformances, such as enums automatically conforming to Hashable and RawRepresentable. Would it make sense to conservatively emit a lookForImplicitConformances result for enums without payloads in order to handle Hashable? Further, can we treat enum MyEnum: String as a lookForSupertype query?

The proposal is still a rough draft, and I'd really appreciate feedback on the overall format, scope and implementation plan. Thanks again for your guidance!


Abstract

We propose reimplementing the compiler’s qualified name lookup in the SwiftSyntax pure-Swift package. Hoisting this API onto the higher-level SwiftSyntax package surfaces essential semantic information, and will enable build tools with more powerful code transformations and more precise diagnostics.

Introduction

Swift Syntax is a Swift package that gives build tools and macros the ability to inspect and manipulate the Swift Abstract Syntax Tree (AST). From formatters to testing and UI-framework macros, the package’s all-Swift parser offers a powerful API that the 2024 GSoC project entitled “Lexical Scopes - A Scope Lookup Library for swift-syntax” expands upon with Unqualified Lookup. Unqualified lookup takes a bare identifier and finds what declaration it refers to; for instance:

extension Array where Element == Int {
  var hasZeros: Bool { 
	  var count = 0
	  for n in self where n == 0 { count += 1 }
	  return count == 0 // ⬅️ Look up `count` here
  }
}

The lookup in the indicated position returns count and issues additional lookup requests. But how would we look up a member-access expression like MyType.f? Qualified lookup allows us to look into a nominal type declaration and find relevant declarations, providing another powerful AST facility that will allow build tools to build even more powerful macros and libraries.

Motivation

The proposed qualified lookup would fill the gap between the simple parsing facilities that SwiftSyntax currently provides and more advanced AST queries for which libraries usually resort to SourceKitten to communicate to the compiler backend. The current approach presents usability and distribution challenges. SourceKitten has to dynamically link the compiler backend’s SourceKit library, causing a multitude of hard-to-debug linker errors, especially on non-MacOS platforms like Linux. As evidenced by SwiftParser, implementing SwiftSyntax entirely in Swift can also increase performance by up to 8%.

There’s already evidence that existing SourceKitten clients want to take advantage of SwiftSyntax’s more advanced AST facilities. For example, SwiftLint is a very popular package utilizing both SourceKitten and SwiftSyntax, which now uses the 2024 GSoC project’s SwiftLexicalScopes library. In the code example above, SwiftLint will issue an unqualified name lookup query to determine if the user is using count == 0 rather than the more idiomatic isEmpty (where the unqualified lookup determines if count is a local variable where we shouldn’t throw an error). However, SwiftLint currently doesn’t check if the type actually contains a count property, a query which we could answer with qualified lookup.

Qualified lookup will provide a pure-Swift, reliable solution for build tools to reason about a type’s members. This lookup will enable new, more powerful macros and bring existing tooling like SwiftLint a step closer to removing SourceKit and resolve the corresponding distribution issues.

Related Work

This project mirrors compiler semantics but operates purely on SwiftSyntax trees without type-checking. Other SourceKit-based projects ultimately invoke the compiler with the aforementioned shortcomings.

Deliverables

Qualified Lookup API

I’ll add a new API to DeclGroupSyntax which includes nominal types, protocols and extensions. This API will include a stateless version and a SymbolTable version that caches results. Note that this project does not attempt overload resolution, constraint solving, operator lookup, or AnyObject dynamic lookup; these are left for the client to handle.

struct SymbolTable {
  /// Construct a table for caching symbol lookup
  /// for the given file syntax.
  init(fileSyntax: SourceFileSyntax)
}

extension DeclGroupSyntax {
	/// Search for members matching given identifier.
	/// Parameters:
	/// - lookUpPosition: Indicates position where we should
	///     start lookup. Will enable future expansion that filters
	///     by access control.
  func lookupMember(
	  _ identifier: Identifier?,
	  from lookUpPosition: AbsolutePosition?,
	  with config: QualifiedLookupConfig = QualifiedLookupConfig()
	) -> [QualifiedLookupResult]
	
	// Same as above but can look up supertypes in SymbolTable
  func lookupMember(
	  _ identifier: Identifier?,
	  from lookUpPosition: AbsolutePosition?,
	  using symbolTable: inout SymbolTable,
	  with config: QualifiedTableLookupConfig = QualifiedTableLookupConfig()
	) -> [QualifiedLookupResult]
}

struct QualifiedLookupConfig {
	init(configuredRegions: ConfiguredRegions? = nil)
}
struct QualifiedTableLookupConfig {
	/// Parameters:
	/// - lookupSuperprotocols: Whether to recursively look up inherited or 
	///     conformed-to protocols.
	/// - lookupSuperclasses: Whether to recursively look up superclasses, and
	///     the protocols they conform to (if `lookupSuperprotocols` is enabled).
	init(lookupSuperprotocols: Bool = true, lookupSuperclasses: Bool = true, 
	     configuredRegions: ConfiguredRegions? = nil)
}

enum QualifiedLookupResult {
  /// Explicitly declared members.
  ///
  /// Includes static/class/instance stored and computed properties,
  /// functions, subscripts and initializers, dynamic-member-lookup
  /// results, along with nested types, type aliases and associated types. 
  //// E.g. 
  /// ```
  /// struct MyStruct<T> { 
  ///   func callAsFunction() {} 
  /// }
  /// ```
  /// Qualified lookup within the `MyStruct` scope would 
  /// return the `callAsFunction()` function declaration. 
  /// Note that qualified lookup won't surface
  /// operator functions, objc functions using dynamic lookup, and generic
  /// parameters like `MyStruct.T` (semantically wrong).
  case members([DeclSyntax], introducedIn: DeclSyntax)
  /// Members declared in conditional extensions, e.g.
  /// ```
  /// extension Array where Element == Int {
  ///   func sum() -> Int { reduce(0, +) } 
  /// }
  /// ```
  /// Qualified lookup will return the `sum()` declaration in
  /// the extension above together with the `where Element == Int` clause.
  case conditionalMembers([DeclSyntax], introducedIn: DeclSyntax, 
         inheritanceClause: InheritanceClauseSyntax?, genericClause: GenericWhereClauseSyntax?)
  /// Implicit members in the given group declaration like `self`,
  /// `Type` and synthesized initializers.
  case implicitMembers([DeclSyntax], introducedIn: DeclGroupSyntaxType)
  /// Types and protocols annotated with `@dynamicMemberLookup` have one or 
  /// more subscripts with a `dynamicMember` string or keypath argument. We defer 
  /// to the type-checker to determine what members these subscripts can produce.
  case lookForDynamicMembers(dynamicMemberSubscripts: [SubscriptDeclSyntax], 
         introducedIn: DeclGroupSyntaxType)
  /// Any unknown attributes that could be attached macros or property wrappers
  /// that expand to more declarations, e.g.
  /// ```swift
  /// struct MyView {
  ///   @State var myState = 0
  /// }
  /// ```
  /// In this case, we instruct the tooling to look up what the `@State`
  /// attribute expands to in the variable declaration above (if anything).
  case lookForMacros(potentialMacroDecl: [DeclSyntax], introducedIn: DeclGroupSyntaxType)
  /// Look for any types we encountered in the lookup that weren't in the
  /// SymbolTable syntax trees (e.g. the looked up type conforms to a 
  /// protocol from a different module).
  case lookForSupertypes(inheritedFrom: InheritanceClauseSyntax, genericClause: GenericWhereClauseSyntax?)
}

The SymbolTable implementation will maintain a mapping of type names to their member declarations. It will also keep track of requests to avoid cycles like the compiler’s existing NameLookupRequests.cpp does.

Integration with Unqualified Lookup

Then, we’ll add SymbolTable support to the existing unqualified lookup API to handle some of the lookForMembers requests:

extension SyntaxProtocol {
  func lookup(
    _ identifier: Identifier?,
	  using symbolTable: inout SymbolTable,
    with config: LookupConfig = LookupConfig()
  ) -> [LookupResult]
}

extension ScopeSyntax {
	func lookup(
    _ identifier: Identifier?,
    at lookUpPosition: AbsolutePosition,
	  using symbolTable: inout SymbolTable,
    with config: LookupConfig
  ) -> [LookupResult]
}

Test Suite

I’ll perform extensive integration testing inspired by the compiler’s NameLookup tests. Further, since we'll surface a stateless API, we can add more targeted tests for specific lookup types; for instance, expect a lookForMacros result instead of immediately resolving the macro expansion. Additionally, I’ll perform simple benchmarks to identify crucial performance bottlenecks.

Documentation

All public APIs will explain their behavior through docstrings, offering examples and outlining edge cases. Additionally, I will add two more articles to the existing SwiftLexicalLookup DocC directory: one to explain the high-level interface with examples, and another describing the lower-level rules of qualified lookup.

Potential Challenges

Due to its expressivity, Swift has a lot of intersecting language features. While this proposal identifies some less evident cases of lookup like dynamicMemberLookup and the handling of macro expansions, there could be other unexpected syntactic constructs we need to handle. Despite its thorough outline, the 2024 Unqualified Lookup GSoC project ran into unexpected issues with guard statements. So since this proposal is similar in scope, I allocated some buffer time to address unexpected issues and provide thorough testing.

Timeline

[Before Coding Starts]

  1. May 4-8: Get to know mentor and discuss next steps.
  2. May 11-15: Take a closer look at NameLookup.cpp and NameLookupRequests.cpp to better understand existing semantics and result caching behavior.
  3. May 18-22: Look at the compiler’s NameLookup test directory for potential tests to include, and experiment with existing SwiftSyntax tests to become more familiar with the codebase.

[Coding Starts]

  1. May 25-29: Set up the qualified lookup API outlined above, write tests for QualifiedLookupResult.members and .implicitMembers; extend unqualified lookup’s testing infrastructure.
  2. June 1-5: Implement qualified lookup for nominal types without a symbol table or configuration; check results using initial tests and add more tests for corner cases.
  3. June 8-12: Extend implementation to protocols and extensions; add .conditionalMembers results; test more edge-cases.
  4. June 15-19: Set up SymbolTable API and add infrastructure for testing #if and .lookForSupertypes results.
  5. June 22-26: Implement SymbolTable API and test; add test cases for .lookForMacros.
  6. June 29 - July 3: Implement the aforementioned .lookupForMacros and .lookForSupertypes results; miscellaneous bug fixes.

[Midterm Evaluation]

  1. July 6-10: Incorporate feedback from midterm evaluation and update plan; clean up and document code.
  2. July 13-17: Add tests for and implement .lookForDynamicMember.
  3. July 20-24: Integrate with existing unqualified lookup API and add integration tests.
  4. July 27-31: Allow time for unforeseen challenges.
  5. August 3-7: Add benchmarks and fix performance issues.
  6. August 10-14: Review code documentation and write documentation articles.
  7. August 17-21: Fix remaining bugs; integrate the updated SwiftLexicalLookup with existing codebase.

[Optional Extension]

  1. August 24 - November 2: Integrate with the compiler’s name lookup and testing infrastructure.
2 Likes

I realized I forgot to ping @xedin in the last post. But, of course, feedback is welcome from everyone reading the proposal!

1 Like

I think I can give some input on that. If I were to approach the project similarly to SwiftLexicalLookup, I’d first try to make the algorithm as simple as possible and prioritize correctness, even if sacrificing some performance along the way, until I have a cohesive model with a sufficient amount of simple unit tests with good coverage. I think performance really begins to be an important factor once you start running the implementation inside the compiler and can benchmark it for some larger, real world inputs. Without that you won’t have access to reliable performance metrics.

As Doug mentioned, hooking up the library to the compiler and validating it alongside of it will likely be an enormous task in itself. The compiler likely does a lot of quirky stuff that will require back and forth between narrowing down failures, fixing the new library, and/or developing new heuristics for the validating algorithm that account for some discrepancies, which is quite time consuming.

That being said, I think it could be an interesting idea to consider generalizing the existing validation algorithm of SwiftLexicalLookup for the purposes of qualified lookup. That could possibly save up some time and make it easier to begin real world testing inside of the compiler. It would likely require separating name matching logic from the part which accounts for quirky ASTScope behavior (which is specific to unqualified lookup), but I think it could be worth it.

Jakub

1 Like

Thanks for chiming in! Sounds good, I’ll work on correctness first.

Are you referring to using the proposed Qualified Lookup API (written in Swift) to essentially replace ASTScope?

Thanks for linking to the validation algorithm. I took a closer look at the ASTGen directory and the extra validation tests that run when enabling UnqualifiedLookupValidation. Validating against the compiler seems like a great fit for the optional extension! If all goes well and I'm granted the extension, I'll try to extend the existing unqualified lookup tests in the compiler to also check for qualified-lookup queries.

I've revised my draft proposal based on Doug and Jakub's suggestions, as well as Robert's feedback on the other Qualified Lookup thread. Here's the draft proposal on Google Docs (open for everyone to comment): Qualified Name Lookup -- GSoC 2026 Proposal - Google Docs.

Any and all feedback is welcome. I'm also pinging @Douglas_Gregor and @xedin in case they didn't see this and are able to provide feedback. Thanks in advance!

Kindly pinging @Douglas_Gregor and @xedin as a reminder :slight_smile: .

1 Like

I have it in my queue, sorry about the delay, I was out for a couple of days and only now back to office.

1 Like

Yeah, of course, no worries. Thanks for taking a look at this!

1 Like

I think replacing the compiler implementation probably won’t be possible within the GSoC timeframe. As far as I know, full replacement still depends on some works in ASTGen (same applies to SwiftLexicalLookup). I only referred to running both qualified lookup implementations in parallel similarly to how the validation algorithm of SwiftLexicalLookup now gets called. It’s still very exciting and would be an important step towards making the API available to other clients (e.g. swift-java interop) before a full replacement inside of the compiler.

2 Likes

Thanks for elaborating. I changed the wording in my proposal to make it clear that the optional extension will only attempt to validate the new qualified lookup API against the compiler.

That's really cool! I didn't know swift-java used name lookup in that way.

2 Likes