-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[ParseableInterface] Expose non-public types required for layout #20004
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
Conversation
0228909
to
55a1be1
Compare
This patch does a pass over the module looking for stored properties that contribute to the parent's storage but that use non-public-or-usableFromInline types. It marks these types for printing, while also hiding their non-required members. It also splits off ShouldPrintForParseableInterface into its own file, because it's grown significantly since it was first introduced in ASTPrinter.
55a1be1
to
0eb2e16
Compare
Now that I’m seeing this written down, I wonder: Could you replace FileHandle with AnyObject? I’m not sure how that would work with non-class types—maybe you could declare it as a tuple of its member types, recursively until you hit public or builtin types? And I’m definitely not sure how it would work with existentials... |
I wanted to avoid going down the rabbit hole of trying to synthesize layout-compatible types, electing for the simple thing of these partially-hidden stubs. |
I actually think we will be able to replace classes that way (though the extra inhabitants are different because AnyObject might be a tagged pointer and a pure Swift reference never will be), but we need basically this for structs and enums, so I think it's okay to move forward this way for now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have some concerns, but this is a good start!
} | ||
|
||
PrintOptions PrintOptions::printParseableInterfaceFile() { | ||
PrintOptions PrintOptions::printParseableInterfaceFile(ModuleDecl *module) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm honestly starting to think this doesn't belong on PrintOptions at all. Only the reusable or generic ones really deserve factory constructors; the rest should live at their use sites. That doesn't have to change in this PR, but it kind of lines up with the spiltting out to a separate file.
return scope.isPublic(); | ||
} | ||
|
||
bool swift::contributesToParentTypeStorage(const AbstractStorageDecl *ASD) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one I think should probably just move onto AbstractStorageDecl, though maybe without the "isResilient" part. But that doesn't have to change now.
@jrose-apple I've updated this PR to also support enums and protocols, and added them to the test. |
57d4a47
to
78581e8
Compare
78581e8
to
90a439a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems nicely along the path we were going down but I have a new concern to work out (see below).
addIfNonPublic(decl); | ||
|
||
// Also walk into the RHS of the typealias to find extra types we need. | ||
decl->getUnderlyingTypeLoc().getType().walk(*this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't the visiting of the decl be responsible for that?
} | ||
|
||
// If we see a nominal type decl, walk its inherited types and generic | ||
// requirements, and continue walking into it children. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: "it children".
|
||
void visitNominalTypeDecl(NominalTypeDecl *decl) { | ||
for (auto inherited : decl->getInherited()) | ||
inherited.getType().walk(findTypeDecls); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uh, hm. If we don't print extensions, we might not get all the protocol conformances, which means we might not satisfy a generic constraint where this type is used. Is that a problem with our plan?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, yes that is a problem. Hmm...should I add extensions of this type to the printed set as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That might work for now. We probably do need to sit down and think about whether printing some kind of "layout token" would be easier all around, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or rather, walk declared conformances in extensions of this type, marking those for inclusion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean, let's go a step further. If these conformances are included, we need the members that satisfy the requirements too, right?
This is starting to sound like it's not an "interface" at all, except for the lack of function bodies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Crude question: have you considered writing this as a request (in the request-evaluator sense)?
@graydon I think the request evaluator would be the wrong model here. Given that this essentially marks a type as required iff it's referenced in a layout-required context, you wouldn't be able to quickly ask the question of a given type. Structuring this as Structuring this as a single walk where we start with all reachable top-level types and push discovered types onto a worklist seemed to be the right answer here. |
Now, @jrose-apple and I discussed offline about the tradeoffs of this method vs. creating layout-compatible types. We came up with a set of rules for non-public-or-
|
I think this is still going to have to be some kind of dummy type. "Print a fake enum with its payloads expanded." |
Going to close this now, as we're going to take a different direction with this. |
This patch does a pass over the module looking for stored properties
that contribute to the parent's storage but that use
non-public-or-usableFromInline types. It marks these types for printing,
while also hiding their non-required members.
For example, in a non-resilient module, a user may write this type:
Since
handle
contributes to the storage of theFile
type, we need to print it inside the declaration ofFile
. Right now, we print the property as:which signifies that the value was meant to be internal and not accessible from client code. But this caused an issue, because
FileHandle
is itselfinternal
, and thus wouldn't be printed. Now, we find decls like FileHandle and elect to print their layout-required members as well. This example would be printed as:Now, we could take this a step further and hide all the members of
FileHandle
, as we know it'll always be heap-allocated and pointer-sized, but that's not part of this patch.It also splits off
ShouldPrintForParseableInterface
into its own file,because it's grown significantly since it was first introduced in
ASTPrinter
.