Skip to content

[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

Merged
merged 1 commit into from
Oct 6, 2018

Conversation

harlanhaskins
Copy link
Contributor

@harlanhaskins harlanhaskins commented Sep 29, 2018

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

@harlanhaskins

This comment has been minimized.

@harlanhaskins harlanhaskins force-pushed the interface-your-fears branch 4 times, most recently from b221403 to ea9cb98 Compare September 29, 2018 00:29
return false;
auto parent = dyn_cast<NominalTypeDecl>(vd->getDeclContext());
if (!parent) return false;
if (parent->isFormallyResilient())
Copy link
Contributor Author

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?

/// There's a very narrow case when we would:
/// If the decl:
/// - has an initializer expression
/// - is @usableFromInline
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also if its public

/// - 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
Copy link
Contributor

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

/// - has an initializer expression
/// - is @usableFromInline
/// If the parent type:
/// - is @_fixed_layout (or effectively fixed-layout)
Copy link
Contributor

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.

Copy link
Contributor Author

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?

Copy link
Contributor

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”.

if (!parent) return false;
if (parent->isFormallyResilient())
return false;
return evaluateOrDefault(vd->getASTContext().evaluator,
Copy link
Contributor

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.

Copy link
Contributor Author

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.

@@ -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=)}}
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then it should be in a separate PR. Tag @rudkx or @xedin for review.

@harlanhaskins harlanhaskins force-pushed the interface-your-fears branch 6 times, most recently from f944888 to 8d06ee8 Compare October 3, 2018 01:23
@harlanhaskins harlanhaskins changed the title [WIP] [InterfaceGen] Print initializers for @usableFromInline properties [InterfaceGen] Print property initializers in resilient, fixed-layout types Oct 3, 2018
@harlanhaskins
Copy link
Contributor Author

@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
Copy link
Contributor Author

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

@@ -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;
Copy link
Contributor

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?

// Don't print @_hasInitialValue if we're in printing an
// @usableFromInline property.
if (auto vd = dyn_cast<VarDecl>(D)) {
if (vd->getAttrs().hasAttribute<UsableFromInlineAttr>())
Copy link
Contributor

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.

if (!parent) return false;
if (!vd->getAttrs().hasAttribute<HasInitialValueAttr>())
return false;
if (!parent->getAttrs().hasAttribute<FixedLayoutAttr>())
Copy link
Contributor

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>())
Copy link
Contributor

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.

Copy link
Contributor Author

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.

StringRef initStr;
SmallString<128> scratch;
if (binding->hasInitStringRepresentation(bindingIndex)) {
initStr = binding->getInitStringRepresentation(bindingIndex, scratch);
Copy link
Contributor

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.

@harlanhaskins harlanhaskins force-pushed the interface-your-fears branch 3 times, most recently from 27cd7aa to 179ac81 Compare October 3, 2018 23:34
@harlanhaskins
Copy link
Contributor Author

@swift-ci please smoke test

@harlanhaskins
Copy link
Contributor Author

@swift-ci please smoke test

Copy link
Contributor

@jrose-apple jrose-apple left a 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.

llvm::PointerIntPair<Pattern *, 3, OptionSet<Flags>> PatternAndFlags;

/// The location of the equal '=' token.
SourceLoc EqualLoc;
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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.

// Don't print @_hasInitialValue if we're printing an initializer
// expression.
if (auto vd = dyn_cast<VarDecl>(D)) {
if (vd->shouldSerializeInitializerText())
Copy link
Contributor

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.

Copy link
Contributor Author

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
Copy link
Contributor

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?

Copy link
Contributor Author

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

Copy link
Contributor

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

Copy link
Contributor Author

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.

Copy link
Contributor

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
Copy link
Contributor

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() {}
}
Copy link
Contributor

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.

@harlanhaskins
Copy link
Contributor Author

@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.
Copy link
Contributor

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

Copy link
Contributor

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.

Copy link
Contributor

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)

Copy link
Contributor

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

Copy link
Contributor

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.

Copy link
Contributor

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.

@harlanhaskins
Copy link
Contributor Author

@swift-ci please smoke test

@harlanhaskins harlanhaskins force-pushed the interface-your-fears branch 3 times, most recently from 425c863 to 54baeed Compare October 5, 2018 23:32
… 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
@harlanhaskins
Copy link
Contributor Author

@swift-ci please smoke test

@harlanhaskins harlanhaskins merged commit 2c86e32 into swiftlang:master Oct 6, 2018
modelorganism pushed a commit to modelorganism/swift that referenced this pull request Oct 11, 2018
… 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
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.

3 participants