Skip to content

Fix: substitute generic type parameters when resolving typealiases#1463

Merged
krzysztofzablocki merged 2 commits into
krzysztofzablocki:masterfrom
shilei365:fix/generic-typealias-parameter-substitution
May 27, 2026
Merged

Fix: substitute generic type parameters when resolving typealiases#1463
krzysztofzablocki merged 2 commits into
krzysztofzablocki:masterfrom
shilei365:fix/generic-typealias-parameter-substitution

Conversation

@shilei365

@shilei365 shilei365 commented May 21, 2026

Copy link
Copy Markdown

Summary

When a generic typealias (e.g. typealias Foo<T> = Bar<T, Never>) is resolved, the placeholder type parameters from the definition were used directly in actualTypeName without substituting the concrete types from the usage site. This caused template code accessing variable.typeName.actualTypeName.generic.typeParameters to see the raw placeholder name (e.g. Value) instead of the actual concrete type (e.g. [String]?).

Fixes #1462

Reproduction

typealias MyPublisher<Value> = AnyPublisher<Value, Never>

protocol DataPublishing {
    var dataPublisher: MyPublisher<[String]?> { get }
}

Before: actualTypeName.generic.typeParameters[0].typeName = Value
After: actualTypeName.generic.typeParameters[0].typeName = [String]?

Difference from #1326

Issue #1326 (fixed by PR #1327) addressed method-level generic type parameters being incorrectly replaced by unrelated global typealiases. That fix added a guard to skip substitution when the type parameter name matches a method's own generic parameter.

This issue is about typealias-level generic parameter forward substitution — an orthogonal code path in actualTypeName(for:containingType:) that was never handled.

Changes

ParserResultsComposed.swift — In actualTypeName(for:containingType:):

  1. Detect when the resolved typealias has generic parameters AND the usage site also has generic parameters
  2. Build a positional mapping from RHS placeholder names to usage-site concrete types (filtering out parameters that are already known types like Never)
  3. Substitute placeholders before constructing the returned TypeName

ComposerSpec.swift — 3 new test cases:

  • Single placeholder substitution (MyPublisher<[String]?>Result<[String]?, Never>)
  • Multiple placeholder substitution (MyResult<String, Error>Result<String, Error>)
  • Concrete RHS types not substituted (Never remains Never when it exists in the type map)

Test Results

  • 3 new test cases added to ComposerSpec.swift
  • All 695 existing tests pass with 0 failures. No regressions.

Lei Shi added 2 commits May 21, 2026 16:44
When a generic typealias (e.g. `typealias Foo<T> = Bar<T, Never>`) is
resolved, the placeholder type parameters from the definition were used
directly in `actualTypeName` without substituting the concrete types
from the usage site. This caused template code accessing
`variable.typeName.actualTypeName.generic.typeParameters` to see the
raw placeholder name (e.g. `Value`) instead of the actual concrete
type (e.g. `[SomeType]?`).

The fix detects when a typealias has generic parameters and maps the
usage-site type arguments to the corresponding placeholder positions
in the typealias RHS, replacing placeholders with concrete types.

Example:
  typealias CurrentValuePublisher<Value> = AnyPublisher<Value, Never>

  protocol MyPublishing {
      var dataPublisher: CurrentValuePublisher<[String]?> { get }
  }

Before: actualTypeName.generic.typeParameters[0].typeName = "Value"
After:  actualTypeName.generic.typeParameters[0].typeName = "[String]?"
Three test cases covering:
- Single placeholder substitution (MyPublisher<[String]?> → Result<[String]?, Never>)
- Multiple placeholder substitution (MyResult<String, Error> → Result<String, Error>)
- Concrete RHS types not substituted (Never remains Never when it exists in type map)
@krzysztofzablocki krzysztofzablocki merged commit bf3a6ed into krzysztofzablocki:master May 27, 2026
1 of 2 checks passed
@krzysztofzablocki

Copy link
Copy Markdown
Owner

thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Generic typealias placeholder types not substituted in actualTypeName

2 participants