Skip to content

Commit c7bdf20

Browse files
authored
Merge pull request swiftlang#70741 from jckarter/pattern-ownership-properties
Add methods to determine the ownership behavior of patterns.
2 parents fd7cbfa + 4011b2f commit c7bdf20

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)