Skip to content

Commit 7d830ce

Browse files
committed
[ConstraintSystem] Add a fix to detect invalid partial application
Currently supported only partial applications of instance methods marked as `mutating`.
1 parent f965d96 commit 7d830ce

File tree

6 files changed

+113
-3
lines changed

6 files changed

+113
-3
lines changed

lib/Sema/CSFix.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,3 +258,14 @@ DefineMemberBasedOnUse::create(ConstraintSystem &cs, Type baseType,
258258
return new (cs.getAllocator())
259259
DefineMemberBasedOnUse(cs, baseType, member, locator);
260260
}
261+
262+
bool AllowInvalidPartialApplication::diagnose(Expr *root, bool asNote) const {
263+
return false;
264+
}
265+
266+
AllowInvalidPartialApplication *
267+
AllowInvalidPartialApplication::create(bool isWarning, ConstraintSystem &cs,
268+
ConstraintLocator *locator) {
269+
return new (cs.getAllocator())
270+
AllowInvalidPartialApplication(isWarning, cs, locator);
271+
}

lib/Sema/CSFix.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,14 @@ enum class FixKind : uint8_t {
104104
/// fix this issue by pretending that member exists and matches
105105
/// given arguments/result types exactly.
106106
DefineMemberBasedOnUse,
107+
108+
/// Allow expressions where 'mutating' method is only partially applied,
109+
/// which means either not applied at all e.g. `Foo.bar` or only `Self`
110+
/// is applied e.g. `foo.bar` or `Foo.bar(&foo)`.
111+
///
112+
/// Allow expressions where initializer call (either `self.init` or
113+
/// `super.init`) is only partially applied.
114+
AllowInvalidPartialApplication,
107115
};
108116

109117
class ConstraintFix {
@@ -501,6 +509,24 @@ class DefineMemberBasedOnUse final : public ConstraintFix {
501509
ConstraintLocator *locator);
502510
};
503511

512+
class AllowInvalidPartialApplication final : public ConstraintFix {
513+
public:
514+
AllowInvalidPartialApplication(bool isWarning, ConstraintSystem &cs,
515+
ConstraintLocator *locator)
516+
: ConstraintFix(cs, FixKind::AllowInvalidPartialApplication, locator,
517+
isWarning) {}
518+
519+
std::string getName() const override {
520+
return "allow partially applied 'mutating' method";
521+
}
522+
523+
bool diagnose(Expr *root, bool asNote = false) const override;
524+
525+
static AllowInvalidPartialApplication *create(bool isWarning,
526+
ConstraintSystem &cs,
527+
ConstraintLocator *locator);
528+
};
529+
504530
} // end namespace constraints
505531
} // end namespace swift
506532

lib/Sema/CSGen.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1633,7 +1633,7 @@ namespace {
16331633
expr->getFunctionRefKind(),
16341634
expr->getOuterAlternatives());
16351635
}
1636-
1636+
16371637
Type visitUnresolvedSpecializeExpr(UnresolvedSpecializeExpr *expr) {
16381638
auto baseTy = CS.getType(expr->getSubExpr());
16391639

lib/Sema/CSSimplify.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5502,6 +5502,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
55025502
case FixKind::AutoClosureForwarding:
55035503
case FixKind::RemoveUnwrap:
55045504
case FixKind::DefineMemberBasedOnUse:
5505+
case FixKind::AllowInvalidPartialApplication:
55055506
llvm_unreachable("handled elsewhere");
55065507
}
55075508

lib/Sema/ConstraintSystem.cpp

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1632,6 +1632,42 @@ resolveOverloadForDeclWithSpecialTypeCheckingSemantics(ConstraintSystem &CS,
16321632
llvm_unreachable("Unhandled DeclTypeCheckingSemantics in switch.");
16331633
}
16341634

1635+
/// \returns true if given declaration is an instance method marked as
1636+
/// `mutating`, false otherwise.
1637+
bool isMutatingMethod(const ValueDecl *decl) {
1638+
if (!(decl->isInstanceMember() && isa<FuncDecl>(decl)))
1639+
return false;
1640+
return cast<FuncDecl>(decl)->isMutating();
1641+
}
1642+
1643+
/// Try to identify and fix failures related to partial function application
1644+
/// e.g. partial application of `init` or 'mutating' instance methods.
1645+
static std::pair<bool, unsigned>
1646+
isInvalidPartialApplication(ConstraintSystem &cs, const ValueDecl *member,
1647+
ConstraintLocator *locator) {
1648+
if (!isMutatingMethod(member))
1649+
return {false, 0};
1650+
1651+
auto *anchor = locator->getAnchor();
1652+
if (!isa<UnresolvedDotExpr>(anchor))
1653+
return {false, 0};
1654+
1655+
// If this choice is a partial application of `init` or
1656+
// `mutating` instance method we should report that it's not allowed.
1657+
auto baseTy = cs.getType(cast<UnresolvedDotExpr>(anchor)->getBase())
1658+
->getWithoutSpecifierType();
1659+
1660+
// If base is a metatype it would be ignored, but if it is
1661+
// some other type it means that we have a single application
1662+
// level already.
1663+
unsigned level = cs.simplifyType(baseTy)->is<MetatypeType>() ? 0 : 1;
1664+
if (auto *call = dyn_cast_or_null<CallExpr>(cs.getParentExpr(anchor))) {
1665+
level += dyn_cast_or_null<CallExpr>(cs.getParentExpr(call)) ? 2 : 1;
1666+
}
1667+
1668+
return {true, level};
1669+
}
1670+
16351671
void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
16361672
Type boundType,
16371673
OverloadChoice choice,
@@ -1872,10 +1908,10 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
18721908
}
18731909
assert(!refType->hasTypeParameter() && "Cannot have a dependent type here");
18741910

1875-
// If we're binding to an init member, the 'throws' need to line up between
1876-
// the bound and reference types.
18771911
if (choice.isDecl()) {
18781912
auto decl = choice.getDecl();
1913+
// If we're binding to an init member, the 'throws' need to line up between
1914+
// the bound and reference types.
18791915
if (auto CD = dyn_cast<ConstructorDecl>(decl)) {
18801916
auto boundFunctionType = boundType->getAs<AnyFunctionType>();
18811917

@@ -1885,6 +1921,36 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
18851921
boundFunctionType->getExtInfo().withThrows());
18861922
}
18871923
}
1924+
1925+
// Check whether applying this overload would result in invalid
1926+
// partial function application e.g. partial application of
1927+
// mutating method or initializer.
1928+
1929+
bool isInvalidPartialApply;
1930+
unsigned level;
1931+
1932+
std::tie(isInvalidPartialApply, level) =
1933+
isInvalidPartialApplication(*this, decl, locator);
1934+
1935+
if (isInvalidPartialApply) {
1936+
// No application at all e.g. `Foo.bar`.
1937+
if (level == 0) {
1938+
// Swift 4 and earlier failed to diagnose a reference to a mutating
1939+
// method without any applications at all, which would get
1940+
// miscompiled into a function with undefined behavior. Warn for
1941+
// source compatibility.
1942+
bool isWarning = !getASTContext().isSwiftVersionAtLeast(5);
1943+
(void)recordFix(
1944+
AllowInvalidPartialApplication::create(isWarning, *this, locator));
1945+
} else if (level == 1) {
1946+
// `Self` parameter is applied, e.g. `foo.bar` or `Foo.bar(&foo)`
1947+
(void)recordFix(AllowInvalidPartialApplication::create(
1948+
/*isWarning=*/false, *this, locator));
1949+
}
1950+
1951+
// Otherwise both `Self` and arguments are applied,
1952+
// e.g. `foo.bar()` or `Foo.bar(&foo)()`, and there is nothing to do.
1953+
}
18881954
}
18891955

18901956
// Note that we have resolved this overload.

lib/Sema/ConstraintSystem.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1775,6 +1775,12 @@ class ConstraintSystem {
17751775
ConstraintLocator *
17761776
getConstraintLocator(const ConstraintLocatorBuilder &builder);
17771777

1778+
/// Lookup and return parent associated with given expression.
1779+
Expr *getParentExpr(Expr *expr) const {
1780+
auto e = ExprWeights.find(expr);
1781+
return e != ExprWeights.end() ? e->second.second : nullptr;
1782+
}
1783+
17781784
public:
17791785

17801786
/// Whether we should attempt to fix problems.

0 commit comments

Comments
 (0)