Skip to content

Commit ea9806d

Browse files
authored
Merge pull request #30097 from slavapestov/sr-75-super-method
Sema: Diagnose unbound method references on 'super.'
2 parents 631555a + 019452f commit ea9806d

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)