Skip to content

Disable destructuring initializations in struct let stored properties. #68930

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
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
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -2175,6 +2175,9 @@ ERROR(pattern_binds_no_variables,none,
ERROR(variable_bound_by_no_pattern,none,
"variable %0 is not bound by any pattern",
(const VarDecl *))
ERROR(destructuring_let_struct_stored_property_unsupported,none,
"binding multiple 'let' stored properties from a single initializer expression in a struct is unsupported",
())

WARNING(optional_ambiguous_case_ref,none,
"assuming you mean '%0.%2'; did you mean '%1.%2' instead?",
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,9 @@ EXPERIMENTAL_FEATURE(NoncopyableGenerics, false)
/// Enables typed throws.
EXPERIMENTAL_FEATURE(TypedThrows, true)

/// Allow destructuring stored `let` bindings in structs.
EXPERIMENTAL_FEATURE(StructLetDestructuring, true)

#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
#undef EXPERIMENTAL_FEATURE
#undef UPCOMING_FEATURE
Expand Down
20 changes: 20 additions & 0 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3488,6 +3488,26 @@ static bool usesFeatureRawLayout(Decl *decl) {
return decl->getAttrs().hasAttribute<RawLayoutAttr>();
}

static bool usesFeatureStructLetDestructuring(Decl *decl) {
auto sd = dyn_cast<StructDecl>(decl);
if (!sd)
return false;

for (auto member : sd->getStoredProperties()) {
if (!member->isLet())
continue;

auto init = member->getParentPattern();
if (!init)
continue;

if (!init->getSingleVar())
return true;
}

return false;
}

static bool hasParameterPacks(Decl *decl) {
if (auto genericContext = decl->getAsGenericContext()) {
auto sig = genericContext->getGenericSignature();
Expand Down
14 changes: 14 additions & 0 deletions lib/SILGen/SILGenConstructor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,13 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF,
.forwardInto(SGF, Loc, init.get());
++elti;
} else {
// TODO: This doesn't correctly take into account destructuring
// pattern bindings on `let`s, for example `let (a, b) = foo()`. In
// cases like that, we ought to evaluate the initializer expression once
// and then do a pattern assignment to the variables in the pattern.
// That case is currently forbidden with an "unsupported" error message
// in Sema.

assert(field->getTypeInContext()->getReferenceStorageReferent()->isEqual(
field->getParentExecutableInitializer()->getType()) &&
"Initialization of field with mismatched type!");
Expand Down Expand Up @@ -534,6 +541,13 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF,
++elti;
} else {
// Otherwise, use its initializer.
// TODO: This doesn't correctly take into account destructuring
// pattern bindings on `let`s, for example `let (a, b) = foo()`. In
// cases like that, we ought to evaluate the initializer expression once
// and then do a pattern assignment to the variables in the pattern.
// That case is currently forbidden with an "unsupported" error message
// in Sema.

assert(field->isParentExecutabledInitialized());
Expr *init = field->getParentExecutableInitializer();

Expand Down
1 change: 1 addition & 0 deletions lib/Sema/TypeCheckConstraints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,7 @@ bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD,
if (hadError)
PBD->setInvalid();
PBD->setInitializerChecked(patternNumber);

return hadError;
}

Expand Down
15 changes: 15 additions & 0 deletions lib/Sema/TypeCheckStorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,21 @@ const PatternBindingEntry *PatternBindingEntryRequest::evaluate(
: GlobalVariable);
}
}

// If the pattern binding appears as a compound stored `let` property with an
// initializer inside of a struct type, diagnose it as unsupported.
// This hasn't ever been implemented properly.
if (!Context.LangOpts.hasFeature(Feature::StructLetDestructuring)
&& !binding->isStatic()
&& binding->isInitialized(entryNumber)
&& isa<StructDecl>(binding->getDeclContext())
&& !pattern->getSingleVar()
&& !vars.empty()
&& vars[0]->isLet()) {
Context.Diags.diagnose(binding->getPattern(entryNumber)->getLoc(),
diag::destructuring_let_struct_stored_property_unsupported);
}

return &pbe;
}

Expand Down
8 changes: 4 additions & 4 deletions test/ModuleInterface/stored-properties-client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

// 1. Build ../stored-properties.swift to a dylib and emit its interface in %t

// RUN: %target-build-swift-dylib(%t/%target-library-name(StoredProperties)) -emit-module-interface-path %t/StoredProperties.swiftinterface %S/stored-properties.swift -module-name StoredProperties -swift-version 5
// RUN: %target-build-swift-dylib(%t/%target-library-name(StoredProperties)) -enable-experimental-feature StructLetDestructuring -emit-module-interface-path %t/StoredProperties.swiftinterface %S/stored-properties.swift -module-name StoredProperties -swift-version 5
// RUN: %target-swift-typecheck-module-from-interface(%t/StoredProperties.swiftinterface) -module-name StoredProperties

// 2. Build this file and link with StoredProperties

// RUN: %target-build-swift %s -I %t -L %t -lStoredProperties -o %t/stored-properties-client %target-rpath(%t)
// RUN: %target-build-swift -enable-experimental-feature StructLetDestructuring %s -I %t -L %t -lStoredProperties -o %t/stored-properties-client %target-rpath(%t)

// 3. Codesign and run this, and ensure it exits successfully.

Expand All @@ -20,9 +20,9 @@

// RUN: %empty-directory(%t)

// RUN: %target-build-swift-dylib(%t/%target-library-name(StoredProperties)) -emit-module-interface-path %t/StoredProperties.swiftinterface %S/stored-properties.swift -module-name StoredProperties -swift-version 5 -enable-library-evolution
// RUN: %target-build-swift-dylib(%t/%target-library-name(StoredProperties)) -enable-experimental-feature StructLetDestructuring -emit-module-interface-path %t/StoredProperties.swiftinterface %S/stored-properties.swift -module-name StoredProperties -swift-version 5 -enable-library-evolution

// RUN: %target-build-swift %s -I %t -L %t -lStoredProperties -o %t/stored-properties-client %target-rpath(%t)
// RUN: %target-build-swift -enable-experimental-feature StructLetDestructuring %s -I %t -L %t -lStoredProperties -o %t/stored-properties-client %target-rpath(%t)
// RUN: %target-codesign %t/stored-properties-client %t/%target-library-name(StoredProperties)
// RUN: %target-run %t/stored-properties-client %t/%target-library-name(StoredProperties)

Expand Down
12 changes: 6 additions & 6 deletions test/ModuleInterface/stored-properties.swift
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
// RUN: %empty-directory(%t)

// RUN: %target-swift-frontend -typecheck -emit-module-interface-path %t.swiftinterface -module-name StoredProperties %s
// RUN: %target-swift-frontend -enable-experimental-feature StructLetDestructuring -typecheck -emit-module-interface-path %t.swiftinterface -module-name StoredProperties %s
// RUN: %target-swift-typecheck-module-from-interface(%t.swiftinterface) -module-name StoredProperties
// RUN: %FileCheck %s < %t.swiftinterface --check-prefix CHECK --check-prefix COMMON

// RUN: %target-swift-frontend -typecheck -emit-module-interface-path %t-resilient.swiftinterface -module-name StoredProperties -enable-library-evolution %s
// RUN: %target-swift-frontend -enable-experimental-feature StructLetDestructuring -typecheck -emit-module-interface-path %t-resilient.swiftinterface -module-name StoredProperties -enable-library-evolution %s
// RUN: %target-swift-typecheck-module-from-interface(%t-resilient.swiftinterface) -module-name StoredProperties
// RUN: %FileCheck %s < %t-resilient.swiftinterface --check-prefix RESILIENT --check-prefix COMMON

// RUN: %target-swift-frontend -emit-module -o %t/Test.swiftmodule -module-name StoredProperties %t.swiftinterface -disable-objc-attr-requires-foundation-module
// RUN: %target-swift-frontend -emit-module -o /dev/null -merge-modules %t/Test.swiftmodule -module-name StoredProperties -emit-module-interface-path - | %FileCheck %s --check-prefix CHECK --check-prefix COMMON
// RUN: %target-swift-frontend -enable-experimental-feature StructLetDestructuring -emit-module -o %t/Test.swiftmodule -module-name StoredProperties %t.swiftinterface -disable-objc-attr-requires-foundation-module
// RUN: %target-swift-frontend -enable-experimental-feature StructLetDestructuring -emit-module -o /dev/null -merge-modules %t/Test.swiftmodule -module-name StoredProperties -emit-module-interface-path - | %FileCheck %s --check-prefix CHECK --check-prefix COMMON

// RUN: %target-swift-frontend -emit-module -o %t/TestResilient.swiftmodule -module-name StoredProperties -enable-library-evolution %t-resilient.swiftinterface -disable-objc-attr-requires-foundation-module
// RUN: %target-swift-frontend -emit-module -o /dev/null -merge-modules %t/TestResilient.swiftmodule -module-name StoredProperties -enable-library-evolution -emit-module-interface-path - | %FileCheck %s --check-prefix RESILIENT --check-prefix COMMON
// RUN: %target-swift-frontend -enable-experimental-feature StructLetDestructuring -emit-module -o %t/TestResilient.swiftmodule -module-name StoredProperties -enable-library-evolution %t-resilient.swiftinterface -disable-objc-attr-requires-foundation-module
// RUN: %target-swift-frontend -enable-experimental-feature StructLetDestructuring -emit-module -o /dev/null -merge-modules %t/TestResilient.swiftmodule -module-name StoredProperties -enable-library-evolution -emit-module-interface-path - | %FileCheck %s --check-prefix RESILIENT --check-prefix COMMON

// COMMON: public struct HasStoredProperties {
public struct HasStoredProperties {
Expand Down
35 changes: 35 additions & 0 deletions test/Sema/struct_property_let_destructuring.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// RUN: %target-swift-frontend -typecheck -verify %s

// https://github.com/apple/swift/issues/68915
// Destructuring initializations for `let` properties in structs isn't
// implemented correctly in SILGen, so diagnose it as unsupported for now.

struct Foo {
var value: Int = 42

let (aaa, bbb) = ("aaa", "bbb") // expected-error{{unsupported}}

let (z1, z2, z3) = ("one", 1, Double.pi) // expected-error{{unsupported}}


func tellMe() {
print(foo.aaa)
print(foo.bbb) // output: aaa


assert(aaa == "aaa")
assert(bbb == "bbb", "bbb should be bbb but it's \(bbb)")
}

}


let foo = Foo(/*value: 1*/)


foo.tellMe()




print("Hello")
3 changes: 2 additions & 1 deletion test/decl/var/properties.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1215,7 +1215,8 @@ _ = r19874152S5() // ok


struct r19874152S6 {
let (a,b) = (1,2) // Cannot handle implicit synth of this yet.
// Cannot handle implicit synth of this yet.
let (a,b) = (1,2) // expected-error {{unsupported}}
}
_ = r19874152S5() // ok

Expand Down