-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[InterfaceGen] Print property initializers in resilient, fixed-layout types #19619
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
[InterfaceGen] Print property initializers in resilient, fixed-layout types #19619
Conversation
442026e
to
becb94d
Compare
This comment has been minimized.
This comment has been minimized.
b221403
to
ea9cb98
Compare
lib/AST/ASTPrinter.cpp
Outdated
return false; | ||
auto parent = dyn_cast<NominalTypeDecl>(vd->getDeclContext()); | ||
if (!parent) return false; | ||
if (parent->isFormallyResilient()) |
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.
@slavapestov This seemed like the right check to do, but is it? I'm trying to see if the type is effectively fixed-layout. Should I be guarding this with a check if the module is resilient?
lib/AST/ASTPrinter.cpp
Outdated
/// There's a very narrow case when we would: | ||
/// If the decl: | ||
/// - has an initializer expression | ||
/// - is @usableFromInline |
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.
Also if its public
lib/AST/ASTPrinter.cpp
Outdated
/// - is @usableFromInline | ||
/// If the parent type: | ||
/// - is @_fixed_layout (or effectively fixed-layout) | ||
/// - has at least one inlinable initializer in itself or any of its |
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 don't think this second check is necessary
lib/AST/ASTPrinter.cpp
Outdated
/// - has an initializer expression | ||
/// - is @usableFromInline | ||
/// If the parent type: | ||
/// - is @_fixed_layout (or effectively fixed-layout) |
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.
What does effectively fixed-layout mean? Only types that have an explicit @_fixed_layout attribute need this treatment. If the module is not resilient and the type is implicitly @_fixed_layout, you cannot print the initial value because it might reference private things. However in that case the initializer symbol is public, so you don't need to emit it yourself.
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.
If the type is explicitly fixed_layout and the property is non-public but usable from inline, should it still print the value?
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.
Yes, and if the property is public too. @usableFromInline makes things public for ABI purposes but not public in the language itself. So “@usableFromInline but not public” is not a useful category of declaration. It’s always “public” or “public or @usableFromInline”.
lib/AST/ASTPrinter.cpp
Outdated
if (!parent) return false; | ||
if (parent->isFormallyResilient()) | ||
return false; | ||
return evaluateOrDefault(vd->getASTContext().evaluator, |
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 don't understand why this needs to be a request. In fact I don't think you need to check this condition at all.
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.
If we are doing the check, it’s nice not to look through the members and extensions for each property of the type. Though, if we don’t need the check all of the request plumbing can go away.
test/Constraints/diagnostics.swift
Outdated
@@ -953,7 +953,7 @@ func SR_6272_a() { | |||
case bar | |||
} | |||
|
|||
// expected-error@+2 {{binary operator '*' cannot be applied to operands of type 'Int' and 'Float'}} {{35-35=Int(}} {{42-42=)}} | |||
// expected-error@+2 {{binary operator '*' cannot be applied to operands of type 'Int' and 'Float'}} {{35-35=Int(}} {{43-43=)}} |
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.
Why did this diagnostic change?
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.
It was previously using an incorrect source location, it was inserting the right paren of the cast before the existing right paren, not after. This was fixed by not removing the outer CoerceExpr when we convert a constructor call to a literal initialization.
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.
f944888
to
8d06ee8
Compare
@slavapestov @jrose-apple I think this is correct now. |
// RUN: %target-swift-frontend -emit-interface-path %t-resilient.swiftinterface -enable-resilience -emit-module -o /dev/null %s | ||
// RUN: %FileCheck %s --check-prefix RESILIENT --check-prefix COMMON < %t-resilient.swiftinterface | ||
|
||
// FIXME(rdar): %target-swift-frontend -emit-module -o %t/Test.swiftmodule %t.swiftinterface -disable-objc-attr-requires-foundation-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 need to actually file this, hold on
include/swift/AST/Decl.h
Outdated
@@ -1883,6 +1883,9 @@ class PatternBindingEntry { | |||
/// The initializer context used for this pattern binding entry. | |||
DeclContext *InitContext = nullptr; | |||
|
|||
/// The text of the initializer expression if deserialized from a module. | |||
StringRef InitStringRepresentation; |
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.
Bitpack nitpick: there are a lot of pattern binding entries in the program. Can we cram this in with the initializer expression, and move the flags to the other members?
lib/AST/ASTPrinter.cpp
Outdated
// Don't print @_hasInitialValue if we're in printing an | ||
// @usableFromInline property. | ||
if (auto vd = dyn_cast<VarDecl>(D)) { | ||
if (vd->getAttrs().hasAttribute<UsableFromInlineAttr>()) |
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 isn't the right check; it should be the same as how you decide whether to print the initializer expression.
lib/AST/ASTPrinter.cpp
Outdated
if (!parent) return false; | ||
if (!vd->getAttrs().hasAttribute<HasInitialValueAttr>()) | ||
return false; | ||
if (!parent->getAttrs().hasAttribute<FixedLayoutAttr>()) |
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.
You also need to check that it's an instance property.
lib/AST/Decl.cpp
Outdated
if (ATD->getProtocol()->getAttrs().hasAttribute<UsableFromInlineAttr>()) | ||
if (auto *containingProto = dyn_cast<ProtocolDecl>(getDeclContext())) { | ||
if (isProtocolRequirement() && | ||
containingProto->getAttrs().hasAttribute<UsableFromInlineAttr>()) |
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 looks like one of my changes. I guess it'll get merged away.
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.
Ah, yes, this was accidental.
lib/Serialization/Serialization.cpp
Outdated
StringRef initStr; | ||
SmallString<128> scratch; | ||
if (binding->hasInitStringRepresentation(bindingIndex)) { | ||
initStr = binding->getInitStringRepresentation(bindingIndex, scratch); |
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 should probably not be serialized if we don't need it (the same conditions as printing).
I guess in general we need that configuration option that says whether we're making a partial module or a complete module, which would be good enough.
27cd7aa
to
179ac81
Compare
@swift-ci please smoke test |
179ac81
to
bcb2100
Compare
@swift-ci please smoke test |
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.
Looks pretty good. All minor comments left.
include/swift/AST/Decl.h
Outdated
llvm::PointerIntPair<Pattern *, 3, OptionSet<Flags>> PatternAndFlags; | ||
|
||
/// The location of the equal '=' token. | ||
SourceLoc EqualLoc; |
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.
Do you think we should put this into the union too? It appeals to my "pack everything as small as possible" instincts, but I can also see us doing a better job preserving source locations through deserialization at some point.
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.
Do we serialize any SourceLocs currently? I feel like we could get the win here by tying the EqualLoc with the InitExpr, and then pay for it later if we do end up serializing this loc. I don't think it would hurt to throw it in the union 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.
We don't today, on the grounds that the serialized module might be distributed somewhere. Okay then.
lib/AST/ASTPrinter.cpp
Outdated
// Don't print @_hasInitialValue if we're printing an initializer | ||
// expression. | ||
if (auto vd = dyn_cast<VarDecl>(D)) { | ||
if (vd->shouldSerializeInitializerText()) |
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 wonder if this indicates that the name shouldn't mention "serialization". What do you think of "isInitExposedToClients" or something? That would also make sense as the condition being checked in Sema when deciding whether to enforce inlinable-like rules.
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.
Yeah, I tried to find a better name that doesn't reference serialization or printing, but I didn't want the name to be something like hasInitInResilientFixedLayoutType
. I like isInitExposedToClients
.
// RUN: %FileCheck %s --check-prefix RESILIENT --check-prefix COMMON < %t-resilient.swiftinterface | ||
|
||
// FIXME(rdar44993525): %target-swift-frontend -emit-module -o %t/Test.swiftmodule %t.swiftinterface -disable-objc-attr-requires-foundation-module | ||
// FIXME(rdar44993525): %target-swift-frontend -emit-module -o /dev/null -merge-modules %t/Test.swiftmodule -module-name Test -emit-interface-path - | %FileCheck %s --check-prefix CHECK --check-prefix COMMON |
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.
For my own curiosity, what goes wrong here?
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.
Since we don't print initializers in a non-resilient module, compiling the emitted interface still fails with:
error: return from initializer without initializing all stored properties
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.
The presence of the @hasInitialValue attribute should be the same as an initializer in a non-resilient module, since the initializer expression has a public entry point in the dylib
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.
Then I should teach SILGen to declare these public symbols when there's a @_hasInitialValue property. I can do that in a follow-up.
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…doesn't seem sufficient to me, because of private fields. How would you know the right private discriminator?
@@ -0,0 +1,67 @@ | |||
// RUN: %empty-directory(%t) | |||
|
|||
// RUN: %target-swift-frontend -emit-interface-path %t.swiftinterface -emit-module -o /dev/null %s |
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 merged my other change, so you can switch these to -typecheck
if you want.
|
||
// COMMON: @inlinable internal init() {} | ||
@inlinable init() {} | ||
} |
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.
Missing a test case for static
and for top-level properties.
bcb2100
to
cc51edb
Compare
@swift-ci please smoke test |
!isPublicOrUsableFromInline(decl)) | ||
!isPublicOrUsableFromInline(decl) && | ||
// FIXME: We need to figure out a way to generate an entry point | ||
// for the initializer expression without revealing the name. |
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 don't think that's possible, since the mangled name of the initializer contains the property name
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.
We don't actually care what the name is, though, since it's not a public entry point. At least, we don't for resilient modules.
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.
If we generate a name here it has to be consistent between invocations though, so that you can do a non-WMO build where multiple source files import the same textual interface (and the cached binary form got rebuild between invocations)
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 would again advocate for the simpler approach, instead of figuring out the exact strategy for when we can elide the name, just be conservative and only elide it if there's no initializer
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.
We're talking about a swiftinterface here; it's always one file.
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.
And we can't just print the name because the name might conflict with extensions.
f94b201
to
43c30f2
Compare
@swift-ci please smoke test |
425c863
to
54baeed
Compare
… types Augment the ASTPrinter to print the name and text of initializer expressions if a property has an initializer and the type is @_fixed_layout and resides in a resilient module, and serialize the text for partial modules. With this change, all .swiftinterface files in the project (except for SwiftLang) compile to swiftmodules on macOS. rdar://43774580 rdar://43812188
54baeed
to
88c3574
Compare
@swift-ci please smoke test |
… types (swiftlang#19619) Augment the ASTPrinter to print the name and text of initializer expressions if a property has an initializer and the type is @_fixed_layout and resides in a resilient module, and serialize the text for partial modules. With this change, all .swiftinterface files in the project (except for SwiftLang) compile to swiftmodules on macOS. rdar://43774580 rdar://43812188
Augment the ASTPrinter to print the name and text of initializer expressions if
a property has an initializer and the type is
@_fixed_layout
and resides in a resilient module, and serialize the text for partial modules.With this change, all .swiftinterface files in the project (except for SwiftLang) compile to swiftmodules on macOS.
rdar://43774580
rdar://43812188