Skip to content

Conversation

@hborla
Copy link
Member

@hborla hborla commented Sep 28, 2020

Support property wrappers on local variables, e.g.

@propertyWrapper
struct Wrapper<T> {
  var wrappedValue: T
}

func test() {
  @Wrapper var value = 10
}

@hborla
Copy link
Member Author

hborla commented Sep 28, 2020

@swift-ci please test source compatibility

@hborla hborla force-pushed the local-property-wrappers branch from 00820e2 to 0842b42 Compare September 28, 2020 23:58
@hborla
Copy link
Member Author

hborla commented Sep 28, 2020

@swift-ci please test source compatibility

body.push_back(returnStmt);
}

// Don't mark local accessors as type-checked - captures still need to be computed.
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems a bit fragile because you're assuming that type checking is idempotent on already-checked AST. Would it be better to insert an explicit call to computeCaptures() inside TypeCheckDeclPrimary.cpp?

Copy link
Contributor

Choose a reason for hiding this comment

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

(And yeah, computeCaptures() should be a request, since there are a few places we have to trigger it manually for synthesized code...)

Copy link
Member Author

Choose a reason for hiding this comment

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

I tried to change this, but I discovered that "re" type checking the accessor is doing more necessary things than just computing captures.

For example, if the wrappedValue getter of a local property wrapper is mutating, buildStorageReference will generate something like for the local getter:

(member_ref_expr implicit type='String' decl=test.(file).Lazy.wrappedValue@test.swift:10:7 [with (substitution_map generic_signature=<Value> (substitution Value -> String))]
  (declref_expr implicit type='Lazy<String>' decl=test.(file).testLocalLazy()._value@test.swift:28:13 direct_to_storage function_ref=unapplied))

But it really needs to look like this:

(load_expr implicit type='String'
  (member_ref_expr implicit type='@lvalue String' decl=test.(file).Lazy.wrappedValue@test.swift:10:7 [with (substitution_map generic_signature=<Value> (substitution Value -> String))]
    (inout_expr implicit type='inout Lazy<String>'
      (declref_expr implicit type='@lvalue Lazy<String>' decl=test.(file).testLocalLazy()._value@test.swift:28:13 direct_to_storage function_ref=unapplied))))

CSApply knows how to do this, but buildStorageReference doesn't. Is there a reason why buildStorageReference is attempting to build a type-checked expression rather than relying on the constraint system to fill in types?

Copy link
Contributor

Choose a reason for hiding this comment

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

Addressing this in a follow-up PR is fine. I suggest investigating why the mutating getter case works for property wrappers on members of types but not with local variables. It might be an easy fix.

checks to check for type context instead of local context.

This generalization will help us implement property wrappers on
global variables, which should use the same approach of not adding
synthesized accessors to the AST and instead lazily visit them in
SILGen.
Copy link
Contributor

@slavapestov slavapestov left a comment

Choose a reason for hiding this comment

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

This looks good to go! I know you haven't written SILGen tests yet but I don't expect to have anything to say about those.

One more thing you might want to do in a follow-up PR is visit auxiliary decls in a couple more places, in case you weren't already planning on it:

  • LookupVisibleDecls, for code completion and typo correction. Eventually LookupVisibleDecls will use ASTScope, but for now it has a duplicate implementation of unqualified lookup
  • SourceEntityWalker for indexing

captured local variables for the assign_by_wrapper setter.

Since assign_by_wrapper will always be re-written to initialization
if the captured local variable is uninitialized, it's unnecessary
to mark the capture as an escape. This lets us support out-of-line
initialization for local property wrappers.
@hborla hborla force-pushed the local-property-wrappers branch from 1d69a1e to 4bb98ba Compare September 30, 2020 20:36

if (auto *projectionVar = getPropertyWrapperProjectionVar())
visit(projectionVar);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should this visit the lazy backing storage as well?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes! I was going to try to implement local lazy in a followup PR

Copy link
Member Author

Choose a reason for hiding this comment

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

(unless somebody else gets to it first 🙂 )

Copy link
Collaborator

Choose a reason for hiding this comment

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

Awesome! There’s an old WIP PR for it: #27600 which you may find helpful.

Copy link
Collaborator

@AnthonyLatsis AnthonyLatsis Sep 30, 2020

Choose a reason for hiding this comment

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

Ah, that one got dropped on the floor because I ended up focusing entirely on figuring out a proper way to insert synthesized decls into a BraceStmt post-factum. They simply get visited here instead, right? I am sort of ashamed of myself for having missed the point, but that's awesome; thanks, Holly!

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, that's right - with this approach, I've added calls to visitAuxiliaryDecls in name lookup, the decl checker, and SILGen, so the synthesized VarDecls don't need to be added to the BraceStmt, but they're still visited in the appropriate places.

@hborla
Copy link
Member Author

hborla commented Sep 30, 2020

@slavapestov could you also take a look at 4bb98ba ? I want to make sure it's not a terrible idea

@hborla hborla marked this pull request as ready for review September 30, 2020 21:00
@hborla
Copy link
Member Author

hborla commented Sep 30, 2020

@swift-ci please smoke test

@hborla
Copy link
Member Author

hborla commented Sep 30, 2020

@swift-ci please test source compatibility

@hborla
Copy link
Member Author

hborla commented Oct 1, 2020

Doesn't look like there were actually any failures in the source compat suite

@hborla
Copy link
Member Author

hborla commented Oct 2, 2020

@swift-ci please smoke test

@hborla hborla merged commit 355fbb3 into swiftlang:main Oct 2, 2020
@hborla hborla deleted the local-property-wrappers branch October 2, 2020 05:45
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.

4 participants