Skip to content

Commit 019452f

Browse files
committed
Sema: Diagnose unbound method references on 'super.'
This is something I noticed by inspection while working on <https://bugs.swift.org/browse/SR-75>. Inside a static method, 'self' is a metatype value, so 'self.instanceMethod' produces an unbound reference of type (Self) -> (Args...) -> Results. You might guess that 'super.instanceMethod' can similarly be used to produce an unbound method reference that calls the superclass method given any 'self' value, but unfortunately it doesn't work. Instead, 'super.instanceMethod' would produce the same result as 'self.instanceMethod'. Maybe we can implement this later, but for now, let's just diagnose the problem. Note that partially-applied method references with 'super.' -- namely, 'self.staticMethod' inside a static context, or 'self.instanceMethod' inside an instance context, continue to work as before. They have the type (Args...) -> Result; since the self value has already been applied we don't hit the representational issue.
1 parent 78b6759 commit 019452f

File tree

7 files changed

+104
-94
lines changed

7 files changed

+104
-94
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3285,14 +3285,16 @@ ERROR(partial_application_of_function_invalid,none,
32853285
"partial application of %select{"
32863286
"'mutating' method|"
32873287
"'super.init' initializer chain|"
3288-
"'self.init' initializer delegation"
3288+
"'self.init' initializer delegation|"
3289+
"'super' instance method with metatype base"
32893290
"}0 is not allowed",
32903291
(unsigned))
32913292
WARNING(partial_application_of_function_invalid_swift4,none,
32923293
"partial application of %select{"
32933294
"'mutating' method|"
32943295
"'super.init' initializer chain|"
3295-
"'self.init' initializer delegation"
3296+
"'self.init' initializer delegation|"
3297+
"'super' instance method with metatype base"
32963298
"}0 is not allowed; calling the function has undefined behavior and will "
32973299
"be an error in future Swift versions",
32983300
(unsigned))

lib/Sema/CSDiagnostics.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3756,6 +3756,8 @@ bool PartialApplicationFailure::diagnoseAsError() {
37563756
anchor, ConstraintLocator::ConstructorMember))) {
37573757
kind = anchor->getBase()->isSuperExpr() ? RefKind::SuperInit
37583758
: RefKind::SelfInit;
3759+
} else if (anchor->getBase()->isSuperExpr()) {
3760+
kind = RefKind::SuperMethod;
37593761
}
37603762

37613763
auto diagnostic = CompatibilityWarning

lib/Sema/CSDiagnostics.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,6 +1091,7 @@ class PartialApplicationFailure final : public FailureDiagnostic {
10911091
MutatingMethod,
10921092
SuperInit,
10931093
SelfInit,
1094+
SuperMethod,
10941095
};
10951096

10961097
bool CompatibilityWarning;

lib/Sema/ConstraintSystem.cpp

Lines changed: 88 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1812,71 +1812,71 @@ static std::pair<Type, Type> getTypeOfReferenceWithSpecialTypeCheckingSemantics(
18121812
llvm_unreachable("Unhandled DeclTypeCheckingSemantics in switch.");
18131813
}
18141814

1815-
/// \returns true if given declaration is an instance method marked as
1816-
/// `mutating`, false otherwise.
1817-
bool isMutatingMethod(const ValueDecl *decl) {
1818-
if (!(decl->isInstanceMember() && isa<FuncDecl>(decl)))
1819-
return false;
1820-
return cast<FuncDecl>(decl)->isMutating();
1821-
}
1822-
1823-
static bool shouldCheckForPartialApplication(ConstraintSystem &cs,
1824-
const ValueDecl *decl,
1825-
ConstraintLocator *locator) {
1826-
auto *anchor = locator->getAnchor();
1827-
if (!(anchor && isa<UnresolvedDotExpr>(anchor)))
1828-
return false;
1829-
1830-
// If this is a reference to instance method marked as 'mutating'
1831-
// it should be checked for invalid partial application.
1832-
if (isMutatingMethod(decl))
1833-
return true;
1834-
1835-
// Another unsupported partial application is related
1836-
// to constructor delegation via `self.init` or `super.init`.
1837-
1838-
if (!isa<ConstructorDecl>(decl))
1839-
return false;
1840-
1841-
auto *UDE = cast<UnresolvedDotExpr>(anchor);
1842-
// This is `super.init`
1843-
if (UDE->getBase()->isSuperExpr())
1844-
return true;
1845-
1846-
// Or this might be `self.init`.
1847-
if (auto *DRE = dyn_cast<DeclRefExpr>(UDE->getBase())) {
1848-
if (auto *baseDecl = DRE->getDecl())
1849-
return baseDecl->getBaseName() == cs.getASTContext().Id_self;
1850-
}
1851-
1852-
return false;
1853-
}
1854-
18551815
/// Try to identify and fix failures related to partial function application
18561816
/// e.g. partial application of `init` or 'mutating' instance methods.
18571817
static std::pair<bool, unsigned>
1858-
isInvalidPartialApplication(ConstraintSystem &cs, const ValueDecl *member,
1818+
isInvalidPartialApplication(ConstraintSystem &cs,
1819+
const AbstractFunctionDecl *member,
18591820
ConstraintLocator *locator) {
1860-
if (!shouldCheckForPartialApplication(cs, member, locator))
1861-
return {false, 0};
1821+
auto *UDE = dyn_cast_or_null<UnresolvedDotExpr>(locator->getAnchor());
1822+
if (UDE == nullptr)
1823+
return {false,0};
18621824

1863-
auto anchor = cast<UnresolvedDotExpr>(locator->getAnchor());
1864-
// If this choice is a partial application of `init` or
1865-
// `mutating` instance method we should report that it's not allowed.
18661825
auto baseTy =
1867-
cs.simplifyType(cs.getType(anchor->getBase()))->getWithoutSpecifierType();
1826+
cs.simplifyType(cs.getType(UDE->getBase()))->getWithoutSpecifierType();
1827+
1828+
auto isInvalidIfPartiallyApplied = [&]() {
1829+
if (auto *FD = dyn_cast<FuncDecl>(member)) {
1830+
// 'mutating' instance methods cannot be partially applied.
1831+
if (FD->isMutating())
1832+
return true;
1833+
1834+
// Instance methods cannot be referenced on 'super' from a static
1835+
// context.
1836+
if (UDE->getBase()->isSuperExpr() &&
1837+
baseTy->is<MetatypeType>() &&
1838+
!FD->isStatic())
1839+
return true;
1840+
}
18681841

1869-
// Partial applications are not allowed only for constructor
1870-
// delegation, reference on the metatype is considered acceptable.
1871-
if (baseTy->is<MetatypeType>() && isa<ConstructorDecl>(member))
1872-
return {false, 0};
1842+
// Another unsupported partial application is related
1843+
// to constructor delegation via 'self.init' or 'super.init'.
1844+
//
1845+
// Note that you can also write 'self.init' or 'super.init'
1846+
// inside a static context -- since 'self' is a metatype there
1847+
// it doesn't have the special delegation meaning that it does
1848+
// in the body of a constructor.
1849+
if (isa<ConstructorDecl>(member) && !baseTy->is<MetatypeType>()) {
1850+
// Check for a `super.init` delegation...
1851+
if (UDE->getBase()->isSuperExpr())
1852+
return true;
1853+
1854+
// ... and `self.init` delegation. Note that in a static context,
1855+
// `self.init` is just an ordinary partial application; it's OK
1856+
// because there's no associated instance for delegation.
1857+
if (auto *DRE = dyn_cast<DeclRefExpr>(UDE->getBase())) {
1858+
if (auto *baseDecl = DRE->getDecl()) {
1859+
if (baseDecl->getBaseName() == cs.getASTContext().Id_self)
1860+
return true;
1861+
}
1862+
}
1863+
}
1864+
1865+
return false;
1866+
};
1867+
1868+
if (!isInvalidIfPartiallyApplied())
1869+
return {false,0};
18731870

18741871
// If base is a metatype it would be ignored (unless this is an initializer
18751872
// call), but if it is some other type it means that we have a single
18761873
// application level already.
1877-
unsigned level = baseTy->is<MetatypeType>() ? 0 : 1;
1878-
if (auto *call = dyn_cast_or_null<CallExpr>(cs.getParentExpr(anchor))) {
1879-
level += dyn_cast_or_null<CallExpr>(cs.getParentExpr(call)) ? 2 : 1;
1874+
unsigned level = 0;
1875+
if (!baseTy->is<MetatypeType>())
1876+
level++;
1877+
1878+
if (auto *call = dyn_cast_or_null<CallExpr>(cs.getParentExpr(UDE))) {
1879+
level += 1;
18801880
}
18811881

18821882
return {true, level};
@@ -2357,39 +2357,41 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
23572357
}
23582358
}
23592359

2360-
// Check whether applying this overload would result in invalid
2361-
// partial function application e.g. partial application of
2362-
// mutating method or initializer.
2363-
2364-
// This check is supposed to be performed without
2365-
// `shouldAttemptFixes` because name lookup can't
2366-
// detect that particular partial application is
2367-
// invalid, so it has to return all of the candidates.
2368-
2369-
bool isInvalidPartialApply;
2370-
unsigned level;
2371-
2372-
std::tie(isInvalidPartialApply, level) =
2373-
isInvalidPartialApplication(*this, decl, locator);
2374-
2375-
if (isInvalidPartialApply) {
2376-
// No application at all e.g. `Foo.bar`.
2377-
if (level == 0) {
2378-
// Swift 4 and earlier failed to diagnose a reference to a mutating
2379-
// method without any applications at all, which would get
2380-
// miscompiled into a function with undefined behavior. Warn for
2381-
// source compatibility.
2382-
bool isWarning = !getASTContext().isSwiftVersionAtLeast(5);
2383-
(void)recordFix(
2384-
AllowInvalidPartialApplication::create(isWarning, *this, locator));
2385-
} else if (level == 1) {
2386-
// `Self` parameter is applied, e.g. `foo.bar` or `Foo.bar(&foo)`
2387-
(void)recordFix(AllowInvalidPartialApplication::create(
2388-
/*isWarning=*/false, *this, locator));
2389-
}
2360+
if (auto *afd = dyn_cast<AbstractFunctionDecl>(decl)) {
2361+
// Check whether applying this overload would result in invalid
2362+
// partial function application e.g. partial application of
2363+
// mutating method or initializer.
2364+
2365+
// This check is supposed to be performed without
2366+
// `shouldAttemptFixes` because name lookup can't
2367+
// detect that particular partial application is
2368+
// invalid, so it has to return all of the candidates.
2369+
2370+
bool isInvalidPartialApply;
2371+
unsigned level;
2372+
2373+
std::tie(isInvalidPartialApply, level) =
2374+
isInvalidPartialApplication(*this, afd, locator);
2375+
2376+
if (isInvalidPartialApply) {
2377+
// No application at all e.g. `Foo.bar`.
2378+
if (level == 0) {
2379+
// Swift 4 and earlier failed to diagnose a reference to a mutating
2380+
// method without any applications at all, which would get
2381+
// miscompiled into a function with undefined behavior. Warn for
2382+
// source compatibility.
2383+
bool isWarning = !getASTContext().isSwiftVersionAtLeast(5);
2384+
(void)recordFix(
2385+
AllowInvalidPartialApplication::create(isWarning, *this, locator));
2386+
} else if (level == 1) {
2387+
// `Self` parameter is applied, e.g. `foo.bar` or `Foo.bar(&foo)`
2388+
(void)recordFix(AllowInvalidPartialApplication::create(
2389+
/*isWarning=*/false, *this, locator));
2390+
}
23902391

2391-
// Otherwise both `Self` and arguments are applied,
2392-
// e.g. `foo.bar()` or `Foo.bar(&foo)()`, and there is nothing to do.
2392+
// Otherwise both `Self` and arguments are applied,
2393+
// e.g. `foo.bar()` or `Foo.bar(&foo)()`, and there is nothing to do.
2394+
}
23932395
}
23942396
}
23952397

test/Constraints/mutating_members.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ func bar() -> Foo.Type { fatalError() }
2727

2828
_ = bar().boom // expected-error{{partial application of 'mutating' method}}
2929
_ = bar().boom(&y) // expected-error{{partial application of 'mutating' method}}
30-
_ = bar().boom(&y)() // ok
30+
_ = bar().boom(&y)() // expected-error{{partial application of 'mutating' method}}
3131

3232
func foo(_ foo: Foo.Type) {
3333
_ = foo.boom // expected-error{{partial application of 'mutating' method}}
3434
_ = foo.boom(&y) // expected-error{{partial application of 'mutating' method}}
35-
_ = foo.boom(&y)() // ok
35+
_ = foo.boom(&y)() // expected-error{{partial application of 'mutating' method}}
3636
}

test/Constraints/mutating_members_compat.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ func bar() -> Foo.Type { fatalError() }
2727

2828
_ = bar().boom // expected-warning{{partial application of 'mutating' method}}
2929
_ = bar().boom(&y) // expected-error{{partial application of 'mutating' method}}
30-
_ = bar().boom(&y)() // ok
30+
_ = bar().boom(&y)() // expected-error{{partial application of 'mutating' method}}
3131

3232
func foo(_ foo: Foo.Type) {
3333
_ = foo.boom // expected-warning{{partial application of 'mutating' method}}
3434
_ = foo.boom(&y) // expected-error{{partial application of 'mutating' method}}
35-
_ = foo.boom(&y)() // ok
35+
_ = foo.boom(&y)() // expected-error{{partial application of 'mutating' method}}
3636
}

test/NameBinding/name_lookup.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,16 +292,19 @@ class ThisDerived1 : ThisBase1 {
292292
super.baseInstanceVar = 42 // expected-error {{member 'baseInstanceVar' cannot be used on type 'ThisBase1'}}
293293
super.baseProp = 42 // expected-error {{member 'baseProp' cannot be used on type 'ThisBase1'}}
294294
super.baseFunc0() // expected-error {{instance member 'baseFunc0' cannot be used on type 'ThisBase1'}}
295-
super.baseFunc0(ThisBase1())()
295+
// expected-error@-1 {{partial application of 'super' instance method with metatype base is not allowed}}
296+
super.baseFunc0(ThisBase1())() // expected-error {{partial application of 'super' instance method with metatype base is not allowed}}
296297
super.baseFunc1(42) // expected-error {{instance member 'baseFunc1' cannot be used on type 'ThisBase1'}}
297-
super.baseFunc1(ThisBase1())(42)
298+
// expected-error@-1 {{partial application of 'super' instance method with metatype base is not allowed}}
299+
super.baseFunc1(ThisBase1())(42) // expected-error {{partial application of 'super' instance method with metatype base is not allowed}}
298300
super[0] = 42.0 // expected-error {{instance member 'subscript' cannot be used on type 'ThisBase1'}}
299301
super.baseStaticVar = 42
300302
super.baseStaticProp = 42
301303
super.baseStaticFunc0()
302304

303305
super.baseExtProp = 42 // expected-error {{member 'baseExtProp' cannot be used on type 'ThisBase1'}}
304306
super.baseExtFunc0() // expected-error {{instance member 'baseExtFunc0' cannot be used on type 'ThisBase1'}}
307+
// expected-error@-1 {{partial application of 'super' instance method with metatype base is not allowed}}
305308
super.baseExtStaticVar = 42 // expected-error {{instance member 'baseExtStaticVar' cannot be used on type 'ThisBase1'}}
306309
super.baseExtStaticProp = 42 // expected-error {{member 'baseExtStaticProp' cannot be used on type 'ThisBase1'}}
307310
super.baseExtStaticFunc0()

0 commit comments

Comments
 (0)