Skip to content

[SymbolGraphGen] only recurse public-private type aliases from Clang #79996

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 31, 2025

Conversation

QuietMisdreavus
Copy link
Contributor

Resolves rdar://145980187

In #78959, i added behavior where a public type alias of a type that is hidden to SymbolGraphGen (i.e. through an underscored prefix or with the @_documentation(visibility:) attribute) would have synthesized children created by copying them over from the underlying type. However, this code did not account for mutually-recursive type alises, like this:

@_documentation(visibility: private)
public class HiddenClassA {
    public typealias ProblematicA = HiddenClassB
    public func funcA() {}
}

@_documentation(visibility: private)
public class HiddenClassB {
    public typealias ProblematicB = HiddenClassA
    public func funcB() {}
}

In this case, the code will continuously find new "public-private type aliases" and infinitely recurse until it overflows the stack.

In order to guard against this pattern, this PR changes the behavior to only perform this recursion for symbols imported from Clang, specifically in the typedef struct pattern highlighted in #78959. This will limit the impact of the recursion only to symbols that are themselves written deeply nested in the source, and should make this kind of mutual-recursion impossible (or at least much less common).

Aside: Alternatives considered

There are a couple alternate approaches i could take with this:

  1. Keep the type alias copying working on Swift symbols, but only go "one level deep", i.e. don't recurse into a type alias if we're already recursing into another type alias
  2. Keep type alias coping for Swift, but keep a stack of parent symbols and block recursion if we would enter a type in our parent stack, i.e. only block recursion in these specific mutual-reference scenarios once we've copied enough symbols to reflect the complete API
  3. Either of the above (or the version in this PR), and also emit a diagnostic when copying is blocked (or when any copying from Swift symbols is happening in the first place, as this implies an API boundary violation as if you were writing a type alias with higher visibility than its underlying type)

The version in this PR feels the most semantically correct, and matches the behavior in Clang ExtractAPI. I would like to incorporate Option 3, but i didn't do it in this PR because i couldn't think of a way to semantically silence the warning while leaving the above pattern intact. I don't want to introduce an unactionable warning for when this situation is deliberately written and the public type alias is actually desired and/or necessary. (And i don't want to introduce a dedicated "silence this warning" variant of the @_documentation attribute because i feel like it breaks the spirit of not having dedicated warning-silencing machinery in Swift writ large.)

@QuietMisdreavus
Copy link
Contributor Author

@swift-ci Please smoke test

@QuietMisdreavus
Copy link
Contributor Author

@swift-ci Please test Windows

@QuietMisdreavus QuietMisdreavus merged commit f5c531c into main Mar 31, 2025
3 checks passed
@QuietMisdreavus QuietMisdreavus deleted the vgm/recursive-type-alias branch March 31, 2025 15:11
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.

2 participants