-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Add @_hasInitialValue to reliably mark variables with initialisers, and use it in TBDGen for StoredPropertyInitializer symbols #18385
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
@swift-ci please smoke test |
(Note to self: Linux failure implies need to add I think I'll change the name of this to |
lib/AST/Decl.cpp
Outdated
auto F = InitAndFlags.getInt(); | ||
if (E) { | ||
InitAndFlags.setInt(F - Flags::Removed); | ||
InitAndFlags.setPointer(E); | ||
if (!PreviousInit) { |
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.
Is there no better place for this, maybe in Sema?
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.
These initialisers get set and unset all over the place. Are you thinking that the attribute should be added once, after all of that?
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 does seem like there's a lot of variables created and not in several places throughout the compiler and REPL, etc., and it seems not all of them go through the "obvious" type checker paths (e.g. property behavior's completePropertyBehaviorStorage
sets everything up perfectly, and skips the main checker). It seems unfortunate to push this detail to be repeated at all such code paths (possibly including in lldb), given it is entirely computable from info given to this call, but I agree that it's a little ugly to have it 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.
Variables created in the REPL never require TBD generation. Same for LLDB. Property behaviors are obsolete and probably getting removed at some point. I don't think any of the derived conformances etc in the type checker have initial values either. Please add this attribute when checking the initial value expression in the type checker.
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've moved it to TypeChecker::typeCheckPatternBinding
.
test/IDE/print_ast_tc_decls.swift
Outdated
@@ -108,7 +108,7 @@ struct d0100_FooStruct { | |||
// PASS_COMMON-LABEL: {{^}}struct d0100_FooStruct {{{$}} | |||
|
|||
var instanceVar1: Int = 0 | |||
// PASS_COMMON-NEXT: {{^}} var instanceVar1: Int{{$}} | |||
// PASS_COMMON-NEXT: {{^}} @_bindingHasInit var instanceVar1: Int{{$}} |
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.
Can you change swift-ide-test to not print this attribute, since it will confuse users who see it in generated interfaces in Xcode?
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.
SourceKit generally ignores it (e.g. it isn't included in the lists of attributes in its RPC protocol thing), but generated interfaces go through a different route?
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.
UserInaccessible takes care of this. swift-ide-test prints all attributes by default, IIRC.
Hm. The information is derivable from whether there are any inlinable initializers, right? Though I guess that's not just a scan of the main declaration for a struct, since they might be declared in extensions. |
I don't think so, we'd also have to look at the body of those initialisers to see how they initialise the properties, e.g.: struct NoSymbol {
var x: Int
@inlineable init() { x = 10 }
}
struct Symbol {
var x: Int = 10
@inlineable init() {}
} Additionally, the current logic has a symbol for the property initialisers whether or not there is an |
There are few things going on here:
|
Ah, okay. It seems this case contradicts your previous point, as it won't be covered by scanning a module for I think I might be missing something? |
Now that I think about it, I suspect we don't want to add public symbols for initial value expressions in a resilient build at all. That would mean adding or removing one would be a breaking change. That in turn suggests serializing them along with the initializer. In the non-resilient case, I think you're right, I got it wrong the first time. We always need these symbols. |
This means we need to do a |
Current behavior:
Note that we already ban designated inits from being added from outside the module, but those cannot reference initial value expressions anyway -- since we have no cross-module knowledge they exist. The only way to reference an initial value expression is to inline an inlinable designated init from outside the module, and the only time the initial value expression creates a public symbol is with a non-resilient build. |
That's also a little scary, since it means that a |
Also how about |
@jrose-apple Nice, I didn't even realize that thing about the 'let'. Good thing we banned this, then. :) Note that it does do the right thing in the cross-file case in non-WMO mode, and it doesn't even have to re-typecheck the expression unless its also used to infer the type. |
(And yes, I'm also in favor of "initial value" since "initializer" is taken.) |
I don't understand what you mean by "fully derivable"? The problem this patch is solving is when generating a TBD file by merging modules, or by looking at the textual interface, there's absolutely no record of an initial value existing. I assume we don't want to have symbols for an initialiser for every property "just in case", that is, we only want symbols for ones with actual initial values. |
e6cb7ac
to
8f60651
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.
Okay, I think this looks pretty good, then, at least for non-resilient libraries. Sorry for misunderstanding the problem and going off in the wrong direction.
include/swift/AST/Attr.def
Outdated
@@ -376,6 +376,10 @@ SIMPLE_DECL_ATTR(_forbidSerializingReference, ForbidSerializingReference, | |||
OnAnyDecl | | |||
LongAttribute | RejectByParser | UserInaccessible | NotSerialized, | |||
77) | |||
SIMPLE_DECL_ATTR(_hasInitialValue, HasInitialValue, | |||
OnVar | | |||
SILOnly | UserInaccessible, |
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.
Oops, I think UserInaccessible
is fine here. No need to limit it to SIL mode; no one's actually going to try to type it.
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.
👍
lib/AST/ASTVerifier.cpp
Outdated
@@ -250,6 +250,16 @@ class Verifier : public ASTWalker { | |||
GenericEnv.push_back({DC}); | |||
} | |||
|
|||
/// \brief Returns the SourceFile for this AST, if it was parsed from a .swift | |||
/// or (if \c allowSIL) .sil 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.
Why this change? That is, what condition doesn't hold for this new attribute?
lib/Sema/CodeSynthesis.cpp
Outdated
@@ -1324,7 +1325,7 @@ void TypeChecker::completePropertyBehaviorStorage(VarDecl *VD, | |||
Storage->setImplicit(); | |||
Storage->setAccess(AccessLevel::Private); | |||
Storage->setSetterAccess(AccessLevel::Private); | |||
|
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.
Sorry, this file only has whitespace changes now; can you remove them?
include/swift/AST/Decl.h
Outdated
: ThePattern(P), EqualLoc(EqualLoc), InitAndFlags(nullptr, {}), | ||
InitContext(InitContext) { | ||
// Setting the initializer does some book-keeping, so we do that separately. | ||
setInit(E); |
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 doesn't seem to be true anymore. Maybe it can go back to the way it was?
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, yeah. This and the change in ASTVerifier and CodeSynthesis are all just left overs from previous versions. Reverted.
lib/TBDGen/TBDGen.cpp
Outdated
entry.getPattern()->forEachVariable([&](VarDecl *VD) { | ||
// Non-global variables might have an explicit initializer symbol. | ||
if (VD->getAttrs().hasAttribute<HasInitialValueAttr>() && | ||
!isGlobalOrStaticVar(VD)) { |
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 no, I forgot that this is also relevant for classes. *sigh* Seems reasonable. Is it worth checking to see if this is a resilient module, though? (ModuleDecl::getResilienceStrategy)
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, might as well do that change here.
8f60651
to
7c3c5ee
Compare
@swift-ci please smoke test |
7c3c5ee
to
c26b31d
Compare
@swift-ci please smoke test |
The information about whether a variable/property is initialized is lost in the public interface, but is, unfortunately, required because it results in a symbol for the initializer (if a class/struct `init` is inlined, it will call initializers for properties that it doesn't initialize itself). This is important to preserve for TBD file generation. Using an attribute rather than just a bit on the VarDecl means this fits into the scheme for module interfaces: textual/valid Swift.
…alizer expression. The initializer expression is lost in the public interface (in Swift modules and the textual interface), but the attribute is preserved.
This checks that all combinations of optimized & non-optimized, and whole-module optimization & incremental compilation give the same result, on a module where this is actually interesting (i.e. has multiple files so the behaviour differs between the two).
c26b31d
to
e952579
Compare
@swift-ci please smoke test |
LGTM |
The information about whether a variable/property is initialized is lost in the
public interface, but is, unfortunately, required because it results in a symbol
for the initializer (if a class/struct
init
is inlined, it will callinitializers for properties that it doesn't initialize itself). This is
important to preserve for TBD file generation.
Using an attribute rather than just a bit on the VarDecl means this fits into
the scheme for module interfaces: textual/valid Swift.
To validate that this is (hopefully) the only case like this, it also adds a test that is the union of all the other TBD tests, meaning multiple interesting files.