Skip to content

Commit 4011b2f

Browse files
committed
Add methods to determine the ownership behavior of patterns.
When matching against a noncopyable value, whether the match operation can borrow the value in-place or needs to take ownership of it is significant. This can generally be determined from the kind of pattern being used, except in the case of expr patterns, where it depends on type-checking the `~=` operator that was used.
1 parent a159604 commit 4011b2f

File tree

7 files changed

+267
-12
lines changed

7 files changed

+267
-12
lines changed

include/swift/AST/Ownership.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,22 @@ optionalityOf(ReferenceOwnership ownership) {
118118
}
119119

120120
/// Different kinds of value ownership supported by Swift.
121+
///
122+
/// The order of these constants is significant. Ascending order indicates
123+
/// stricter requirements on the value to be used with the given ownership
124+
/// kind: any value can be accessed with a shared borrow (so long as there
125+
/// are no exclusive accesses overlapping). An exclusive `inout` borrow is
126+
/// only possible for mutable values which the caller already has exclusive
127+
/// access to or ownership of. Consumption of an owned value requires sole
128+
/// ownership of that value.
121129
enum class ValueOwnership : uint8_t {
122130
/// the context-dependent default ownership (sometimes shared,
123131
/// sometimes owned)
124132
Default,
125-
/// an 'inout' exclusive, mutating borrow
126-
InOut,
127133
/// a 'borrowing' nonexclusive, usually nonmutating borrow
128134
Shared,
135+
/// an 'inout' exclusive, mutating borrow
136+
InOut,
129137
/// a 'consuming' ownership transfer
130138
Owned,
131139

include/swift/AST/Pattern.h

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,16 @@ class alignas(8) Pattern : public ASTAllocated<Pattern> {
230230

231231
/// Does this pattern have any mutable 'var' bindings?
232232
bool hasAnyMutableBindings() const;
233+
234+
/// Get the ownership behavior of this pattern on the value being matched
235+
/// against it.
236+
///
237+
/// The pattern must be type-checked for this operation to be valid. If
238+
/// \c mostRestrictiveSubpatterns is non-null, the pointed-to vector will be
239+
/// populated with references to the subpatterns that cause the pattern to
240+
/// have stricter than "shared" ownership behavior for diagnostic purposes.
241+
ValueOwnership getOwnership(
242+
SmallVectorImpl<Pattern*> *mostRestrictiveSubpatterns = nullptr) const;
233243

234244
static bool classof(const Pattern *P) { return true; }
235245

@@ -685,8 +695,10 @@ class ExprPattern : public Pattern {
685695
DeclContext *DC;
686696

687697
/// A synthesized call to the '~=' operator comparing the match expression
688-
/// on the left to the matched value on the right.
689-
mutable Expr *MatchExpr = nullptr;
698+
/// on the left to the matched value on the right, pairend with a record of the
699+
/// ownership of the subject operand.
700+
mutable llvm::PointerIntPair<Expr *, 2, ValueOwnership>
701+
MatchExprAndOperandOwnership{nullptr, ValueOwnership::Default};
690702

691703
/// An implicit variable used to represent the RHS value of the synthesized
692704
/// match expression.
@@ -698,6 +710,8 @@ class ExprPattern : public Pattern {
698710

699711
friend class ExprPatternMatchRequest;
700712

713+
void updateMatchExpr(Expr *matchExpr) const;
714+
701715
public:
702716
/// Create a new parsed unresolved ExprPattern.
703717
static ExprPattern *createParsed(ASTContext &ctx, Expr *E, DeclContext *DC);
@@ -711,7 +725,6 @@ class ExprPattern : public Pattern {
711725

712726
Expr *getSubExpr() const { return SubExprAndIsResolved.getPointer(); }
713727
void setSubExpr(Expr *e) { SubExprAndIsResolved.setPointer(e); }
714-
715728
DeclContext *getDeclContext() const { return DC; }
716729

717730
void setDeclContext(DeclContext *newDC) {
@@ -722,7 +735,17 @@ class ExprPattern : public Pattern {
722735

723736
/// The match expression if it has been computed, \c nullptr otherwise.
724737
/// Should only be used by the ASTDumper and ASTWalker.
725-
Expr *getCachedMatchExpr() const { return MatchExpr; }
738+
Expr *getCachedMatchExpr() const {
739+
return MatchExprAndOperandOwnership.getPointer();
740+
}
741+
742+
/// Return the ownership of the subject parameter for the `~=` operator being
743+
/// used (and thereby, the ownership of the pattern match itself), or
744+
/// \c Default if the ownership of the parameter is unresolved.
745+
ValueOwnership getCachedMatchOperandOwnership() const {
746+
auto ownership = MatchExprAndOperandOwnership.getInt();
747+
return ownership;
748+
}
726749

727750
/// The match variable if it has been computed, \c nullptr otherwise.
728751
/// Should only be used by the ASTDumper and ASTWalker.
@@ -731,14 +754,18 @@ class ExprPattern : public Pattern {
731754
/// A synthesized call to the '~=' operator comparing the match expression
732755
/// on the left to the matched value on the right.
733756
Expr *getMatchExpr() const;
757+
ValueOwnership getMatchOperandOwnership() const {
758+
(void)getMatchExpr();
759+
return getCachedMatchOperandOwnership();
760+
}
734761

735762
/// An implicit variable used to represent the RHS value of the synthesized
736763
/// match expression.
737764
VarDecl *getMatchVar() const;
738765

739766
void setMatchExpr(Expr *e) {
740-
assert(MatchExpr && "Should only update an existing MatchExpr");
741-
MatchExpr = e;
767+
assert(getCachedMatchExpr() && "Should only update an existing MatchExpr");
768+
updateMatchExpr(e);
742769
}
743770

744771
SourceLoc getLoc() const;

lib/AST/ASTDumper.cpp

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -928,7 +928,7 @@ namespace {
928928
using PrintBase::PrintBase;
929929

930930
void printCommon(Pattern *P, const char *Name, StringRef Label) {
931-
printHead(Name, PatternColor, Label);
931+
printHead(Name, PatternColor, Label);
932932

933933
printFlag(P->isImplicit(), "implicit", ExprModifierColor);
934934

@@ -990,6 +990,22 @@ namespace {
990990
}
991991
void visitExprPattern(ExprPattern *P, StringRef label) {
992992
printCommon(P, "pattern_expr", label);
993+
switch (P->getCachedMatchOperandOwnership()) {
994+
case ValueOwnership::Default:
995+
break;
996+
case ValueOwnership::Shared:
997+
printFieldRaw([](llvm::raw_ostream &os) { os << "borrowing"; },
998+
"ownership");
999+
break;
1000+
case ValueOwnership::InOut:
1001+
printFieldRaw([](llvm::raw_ostream &os) { os << "mutating"; },
1002+
"ownership");
1003+
break;
1004+
case ValueOwnership::Owned:
1005+
printFieldRaw([](llvm::raw_ostream &os) { os << "consuming"; },
1006+
"ownership");
1007+
break;
1008+
}
9931009
if (auto m = P->getCachedMatchExpr())
9941010
printRec(m);
9951011
else
@@ -2011,6 +2027,22 @@ class PrintStmt : public StmtVisitor<PrintStmt, void, StringRef>,
20112027
printFlag(LabelItem.isDefault(), "default");
20122028

20132029
if (auto *CasePattern = LabelItem.getPattern()) {
2030+
switch (CasePattern->getOwnership()) {
2031+
case ValueOwnership::Default:
2032+
break;
2033+
case ValueOwnership::Shared:
2034+
printFieldRaw([](llvm::raw_ostream &os) { os << "borrowing"; },
2035+
"ownership");
2036+
break;
2037+
case ValueOwnership::InOut:
2038+
printFieldRaw([](llvm::raw_ostream &os) { os << "mutating"; },
2039+
"ownership");
2040+
break;
2041+
case ValueOwnership::Owned:
2042+
printFieldRaw([](llvm::raw_ostream &os) { os << "consuming"; },
2043+
"ownership");
2044+
break;
2045+
}
20142046
printRec(CasePattern);
20152047
}
20162048
if (auto *Guard = LabelItem.getGuardExpr()) {

lib/AST/Pattern.cpp

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "swift/AST/Pattern.h"
1818
#include "swift/AST/ASTContext.h"
19+
#include "swift/AST/ASTVisitor.h"
1920
#include "swift/AST/ASTWalker.h"
2021
#include "swift/AST/Expr.h"
2122
#include "swift/AST/GenericEnvironment.h"
@@ -569,6 +570,49 @@ VarDecl *ExprPattern::getMatchVar() const {
569570
.getMatchVar();
570571
}
571572

573+
void ExprPattern::updateMatchExpr(Expr *e) const {
574+
class FindMatchOperatorDeclRef: public ASTWalker {
575+
public:
576+
ValueOwnership Ownership = ValueOwnership::Default;
577+
578+
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
579+
// See if this is the reference to the ~= operator used.
580+
auto declRef = dyn_cast<DeclRefExpr>(E);
581+
if (!declRef) {
582+
return Action::Continue(E);
583+
}
584+
auto decl = declRef->getDecl();
585+
auto declName = decl->getName();
586+
if (!declName.isOperator()) {
587+
return Action::Continue(E);
588+
}
589+
590+
if (!declName.getBaseIdentifier().is("~=")) {
591+
return Action::Continue(E);
592+
}
593+
594+
// We found a `~=` declref. Get the value ownership from the parameter.
595+
auto fnTy = decl->getInterfaceType()->castTo<AnyFunctionType>();
596+
if (decl->isStatic()) {
597+
fnTy = fnTy->getResult()->castTo<AnyFunctionType>();
598+
}
599+
// Subject value is the right-hand operand to the operator.
600+
assert(fnTy->getParams().size() == 2);
601+
Ownership = fnTy->getParams()[1].getValueOwnership();
602+
// Operators are always normal functions or methods, so their default
603+
// parameter ownership is always borrowing.
604+
if (Ownership == ValueOwnership::Default) {
605+
Ownership = ValueOwnership::Shared;
606+
}
607+
return Action::Stop();
608+
}
609+
};
610+
FindMatchOperatorDeclRef walker;
611+
e->walk(walker);
612+
613+
MatchExprAndOperandOwnership = {e, walker.Ownership};
614+
}
615+
572616
SourceLoc EnumElementPattern::getStartLoc() const {
573617
return (ParentType && !ParentType->isImplicit())
574618
? ParentType->getSourceRange().Start
@@ -680,3 +724,89 @@ void swift::simple_display(llvm::raw_ostream &out, const Pattern *pattern) {
680724
SourceLoc swift::extractNearestSourceLoc(const Pattern *pattern) {
681725
return pattern->getLoc();
682726
}
727+
728+
ValueOwnership
729+
Pattern::getOwnership(
730+
SmallVectorImpl<Pattern *> *mostRestrictiveSubpatterns) const
731+
{
732+
class GetPatternOwnership: public PatternVisitor<GetPatternOwnership, void> {
733+
public:
734+
ValueOwnership Ownership = ValueOwnership::Shared;
735+
SmallVectorImpl<Pattern *> *RestrictingPatterns = nullptr;
736+
737+
void increaseOwnership(ValueOwnership newOwnership, Pattern *p) {
738+
// If the new ownership is stricter than the current ownership, then
739+
// clear the restricting patterns we'd collected and start over with the
740+
// new stricter ownership.
741+
if (newOwnership > Ownership) {
742+
Ownership = newOwnership;
743+
if (RestrictingPatterns) {
744+
RestrictingPatterns->clear();
745+
}
746+
}
747+
748+
if (RestrictingPatterns
749+
&& newOwnership == Ownership
750+
&& Ownership > ValueOwnership::Shared) {
751+
RestrictingPatterns->push_back(p);
752+
}
753+
}
754+
755+
#define USE_SUBPATTERN(Kind) \
756+
void visit##Kind##Pattern(Kind##Pattern *pattern) { \
757+
return visit(pattern->getSubPattern()); \
758+
}
759+
760+
USE_SUBPATTERN(Paren)
761+
USE_SUBPATTERN(Typed)
762+
USE_SUBPATTERN(Binding)
763+
#undef USE_SUBPATTERN
764+
void visitTuplePattern(TuplePattern *p) {
765+
for (auto &element : p->getElements()) {
766+
visit(element.getPattern());
767+
}
768+
}
769+
770+
void visitNamedPattern(NamedPattern *p) {
771+
// `var` and `let` bindings consume the matched value.
772+
// TODO: borrowing/mutating/consuming parameters
773+
increaseOwnership(ValueOwnership::Owned, p);
774+
}
775+
776+
void visitAnyPattern(AnyPattern *p) {
777+
/* no change */
778+
}
779+
void visitBoolPattern(BoolPattern *p) {
780+
/* no change */
781+
}
782+
783+
void visitIsPattern(IsPattern *p) {
784+
// Casting currently always consumes.
785+
// TODO: Sometimes maybe it doesn't need to be.
786+
increaseOwnership(ValueOwnership::Owned, p);
787+
}
788+
789+
void visitEnumElementPattern(EnumElementPattern *p) {
790+
if (p->hasSubPattern()) {
791+
visit(p->getSubPattern());
792+
}
793+
}
794+
795+
void visitOptionalSomePattern(OptionalSomePattern *p) {
796+
visit(p->getSubPattern());
797+
}
798+
799+
void visitExprPattern(ExprPattern *p) {
800+
// We can't get the ownership reliably if the pattern hasn't been resolved.
801+
if (!p->isResolved()) {
802+
return;
803+
}
804+
increaseOwnership(p->getMatchOperandOwnership(), p);
805+
}
806+
};
807+
808+
GetPatternOwnership visitor;
809+
visitor.RestrictingPatterns = mostRestrictiveSubpatterns;
810+
visitor.visit(const_cast<Pattern *>(this));
811+
return visitor.Ownership;
812+
}

lib/AST/TypeCheckRequests.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,13 +1029,13 @@ ExprPatternMatchRequest::getCachedResult() const {
10291029
if (!EP->MatchVar)
10301030
return llvm::None;
10311031

1032-
return ExprPatternMatchResult(EP->MatchVar, EP->MatchExpr);
1032+
return ExprPatternMatchResult(EP->MatchVar, EP->getCachedMatchExpr());
10331033
}
10341034

10351035
void ExprPatternMatchRequest::cacheResult(ExprPatternMatchResult result) const {
10361036
auto *EP = std::get<0>(getStorage());
10371037
EP->MatchVar = result.getMatchVar();
1038-
EP->MatchExpr = result.getMatchExpr();
1038+
EP->MatchExprAndOperandOwnership.setPointer(result.getMatchExpr());
10391039
}
10401040

10411041
//----------------------------------------------------------------------------//

lib/Serialization/ModuleFormat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5858
/// describe what change you made. The content of this comment isn't important;
5959
/// it just ensures a conflict if two people change the module format.
6060
/// Don't worry about adhering to the 80-column limit for this line.
61-
const uint16_t SWIFTMODULE_VERSION_MINOR = 831; // mark_dependence [nonescaping]
61+
const uint16_t SWIFTMODULE_VERSION_MINOR = 832; // reorder ValueOwnership
6262

6363
/// A standard hash seed used for all string hashes in a serialized module.
6464
///

test/Sema/switch-ownership.swift

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// RUN: %target-swift-frontend -typecheck -dump-ast %s | %FileCheck %s
2+
3+
struct A {
4+
static func ~=(pattern: B, subject: A) -> Bool { return true }
5+
static func ~=(pattern: C, subject: borrowing A) -> Bool { return true }
6+
static func ~=(pattern: D, subject: consuming A) -> Bool { return true }
7+
}
8+
struct B { }
9+
struct C { }
10+
struct D { }
11+
struct E { }
12+
struct F { }
13+
struct G { }
14+
15+
func ~=(pattern: E, subject: A) -> Bool { return true }
16+
func ~=(pattern: F, subject: borrowing A) -> Bool { return true }
17+
func ~=(pattern: G, subject: consuming A) -> Bool { return true }
18+
19+
// CHECK-LABEL: (func_decl{{.*}} "test(value:)"
20+
func test(value: A) {
21+
// CHECK: (switch_stmt
22+
switch value {
23+
// CHECK: (case_stmt
24+
// CHECK: (case_label_item ownership=borrowing
25+
// CHECK: (pattern_expr type="A" ownership=borrowing
26+
case B():
27+
break
28+
// CHECK: (case_stmt
29+
// CHECK: (case_label_item ownership=borrowing
30+
// CHECK: (pattern_expr type="A" ownership=borrowing
31+
case C():
32+
break
33+
// CHECK: (case_stmt
34+
// CHECK: (case_label_item ownership=consuming
35+
// CHECK: (pattern_expr type="A" ownership=consuming
36+
case D():
37+
break
38+
// CHECK: (case_stmt
39+
// CHECK: (case_label_item ownership=borrowing
40+
// CHECK: (pattern_expr type="A" ownership=borrowing
41+
case E():
42+
break
43+
// CHECK: (case_stmt
44+
// CHECK: (case_label_item ownership=borrowing
45+
// CHECK: (pattern_expr type="A" ownership=borrowing
46+
case F():
47+
break
48+
// CHECK: (case_stmt
49+
// CHECK: (case_label_item ownership=consuming
50+
// CHECK: (pattern_expr type="A" ownership=consuming
51+
case G():
52+
break
53+
// CHECK: (case_stmt
54+
// CHECK: (case_label_item default ownership=borrowing
55+
default:
56+
break
57+
}
58+
}

0 commit comments

Comments
 (0)