Skip to content

[CSClosure] Add support for projected/wrapper values #60384

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 2 commits into from
Aug 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -5447,6 +5447,20 @@ class VarDecl : public AbstractStorageDecl {
/// is provided first.
llvm::TinyPtrVector<CustomAttr *> getAttachedPropertyWrappers() const;

/// Retrieve the outermost property wrapper attribute associated with
/// this declaration. For example:
///
/// \code
/// @A @B @C var <name>: Bool = ...
/// \endcode
///
/// The outermost attribute in this case is `@A` and it has
/// complete wrapper type `A<B<C<Bool>>>`.
CustomAttr *getOutermostAttachedPropertyWrapper() const {
auto wrappers = getAttachedPropertyWrappers();
return wrappers.empty() ? nullptr : wrappers.front();
}

/// Whether this property has any attached property wrappers.
bool hasAttachedPropertyWrapper() const;

Expand Down
2 changes: 1 addition & 1 deletion lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6600,7 +6600,7 @@ bool VarDecl::hasExternalPropertyWrapper() const {
return true;

// Wrappers with attribute arguments are always implementation-detail.
if (getAttachedPropertyWrappers().front()->hasArgs())
if (getOutermostAttachedPropertyWrapper()->hasArgs())
return false;

auto wrapperInfo = getAttachedPropertyWrapperTypeInfo(0);
Expand Down
6 changes: 3 additions & 3 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8578,8 +8578,8 @@ static Optional<SolutionApplicationTarget> applySolutionToInitialization(
wrappedVar, initType->mapTypeOutOfContext());

// Record the semantic initializer on the outermost property wrapper.
wrappedVar->getAttachedPropertyWrappers().front()
->setSemanticInit(initializer);
wrappedVar->getOutermostAttachedPropertyWrapper()->setSemanticInit(
initializer);

// If this is a wrapped parameter, we're done.
if (isa<ParamDecl>(wrappedVar))
Expand Down Expand Up @@ -8997,7 +8997,7 @@ ExprWalker::rewriteTarget(SolutionApplicationTarget target) {
return target;
} else if (auto *wrappedVar = target.getAsUninitializedWrappedVar()) {
// Get the outermost wrapper type from the solution
auto outermostWrapper = wrappedVar->getAttachedPropertyWrappers().front();
auto outermostWrapper = wrappedVar->getOutermostAttachedPropertyWrapper();
auto backingType = solution.simplifyType(
solution.getType(outermostWrapper->getTypeExpr()));

Expand Down
23 changes: 23 additions & 0 deletions lib/Sema/CSClosure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,29 @@ class TypeVariableRefFinder : public ASTWalker {
if (!var)
return {true, expr};

if (auto *wrappedVar = var->getOriginalWrappedProperty()) {
auto outermostWrapperAttr =
wrappedVar->getOutermostAttachedPropertyWrapper();

// If the attribute doesn't have a type it could only mean
// that the declaration was incorrect.
if (!CS.hasType(outermostWrapperAttr->getTypeExpr()))
return {true, expr};

auto wrapperType =
CS.simplifyType(CS.getType(outermostWrapperAttr->getTypeExpr()));

if (var->getName().hasDollarPrefix()) {
// $<name> is the projected value var
CS.setType(var, computeProjectedValueType(wrappedVar, wrapperType));
} else {
// _<name> is the wrapper var
CS.setType(var, computeWrappedValueType(wrappedVar, wrapperType));
}

return {true, expr};
}

// If there is no type recorded yet, let's check whether
// it is a placeholder variable implicitly generated by the
// compiler.
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3595,7 +3595,7 @@ bool InvalidProjectedValueArgument::diagnoseAsError() {
if (!param->hasAttachedPropertyWrapper()) {
param->diagnose(diag::property_wrapper_param_no_wrapper, param->getName());
} else if (!param->hasImplicitPropertyWrapper() &&
param->getAttachedPropertyWrappers().front()->hasArgs()) {
param->getOutermostAttachedPropertyWrapper()->hasArgs()) {
param->diagnose(diag::property_wrapper_param_attr_arg);
} else {
Type backingType;
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9972,7 +9972,7 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar,
wrappedValueType = createTypeVariable(getConstraintLocator(paramDecl),
TVO_CanBindToHole | TVO_CanBindToLValue);
} else {
auto *wrapperAttr = paramDecl->getAttachedPropertyWrappers().front();
auto *wrapperAttr = paramDecl->getOutermostAttachedPropertyWrapper();
auto wrapperType = paramDecl->getAttachedPropertyWrapperType(0);
backingType = replaceInferableTypesWithTypeVars(
wrapperType, getConstraintLocator(wrapperAttr->getTypeRepr()));
Expand Down
4 changes: 2 additions & 2 deletions lib/Sema/ConstraintSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6433,9 +6433,9 @@ void ConstraintSystem::diagnoseFailureFor(SolutionApplicationTarget target) {
DE.diagnose(expr->getLoc(), diag::type_of_expression_is_ambiguous)
.highlight(expr->getSourceRange());
} else if (auto *wrappedVar = target.getAsUninitializedWrappedVar()) {
auto *wrapper = wrappedVar->getAttachedPropertyWrappers().back();
auto *outerWrapper = wrappedVar->getOutermostAttachedPropertyWrapper();
Type propertyType = wrappedVar->getInterfaceType();
Type wrapperType = wrapper->getType();
Type wrapperType = outerWrapper->getType();

// Emit the property wrapper fallback diagnostic
wrappedVar->diagnose(diag::property_wrapper_incompatible_property,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// RUN: %target-typecheck-verify-swift

// https://github.com/apple/swift/issues/59294
// https://github.com/apple/swift/issues/59295

@propertyWrapper
struct WrapperValue<Value> {
Expand All @@ -25,18 +26,94 @@ struct WrapperValue<Value> {
func printValue() {
print(value)
}

func returnValue() -> Value {
return value
}
}

@propertyWrapper
struct OuterWrapper<Value> {
var value: Value
init(wrappedValue: Value) {
self.value = wrappedValue
}

var projectedValue: Self {
return self
}

var wrappedValue: Value {
get {
self.value
}
set {
self.value = newValue
}
}
}

class Test {
static func test() {
return [0, 1, 2].compactMap { _ in // expected-error {{unexpected non-void return value in void function}} expected-note {{did you mean to add a return type?}}
@WrapperValue var value: Bool? = false
if value != nil {
$value.printValue()
return false
}

return value ?? false
}
}
static func testProjectedAndWrapperVars() {
func test(_: (Int) -> Bool) {}
func test(_: (String) -> Void) {}

test {
@WrapperValue var value = $0
if $value.returnValue() > 0 {
test {
return $value.returnValue() == $0 &&
_value == $0
}
}
return false
}

test {
@WrapperValue var value = $0

test {
if _value != $0 { // Ok
$value.printValue()
}
}
}
}

static func testNestedWrappers() {
func test(_: (Bool) -> Void) {}
func test(_: () -> Void) {}

test {
if true {
@OuterWrapper @WrapperValue var value = $0
if true {
let _: Bool = _value == $0
let _: OuterWrapper<WrapperValue<Bool>> = $value
let _: Bool = value
}
}
}
}

static func invalidVar() {
_ = [0, 1, 2].compactMap {
@WrapperValue var value: Bool? = $0
// expected-error@-1 {{cannot convert value of type 'Int' to specified type 'Bool?'}}
if true {
$value.printValue()
}
}
}
}