Skip to content

Commit 8bfefc1

Browse files
committed
[5.1] Sema: Infer @objc for dynamic replacement
rdar://48259565
1 parent 3adc814 commit 8bfefc1

File tree

6 files changed

+157
-49
lines changed

6 files changed

+157
-49
lines changed

lib/Sema/TypeCheckAttr.cpp

Lines changed: 83 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2036,8 +2036,8 @@ void AttributeChecker::visitDiscardableResultAttr(DiscardableResultAttr *attr) {
20362036

20372037
/// Lookup the replaced decl in the replacments scope.
20382038
void lookupReplacedDecl(DeclName replacedDeclName,
2039-
DynamicReplacementAttr *attr,
2040-
AbstractFunctionDecl *replacement,
2039+
const DynamicReplacementAttr *attr,
2040+
const ValueDecl *replacement,
20412041
SmallVectorImpl<ValueDecl *> &results) {
20422042
auto *declCtxt = replacement->getDeclContext();
20432043

@@ -2047,9 +2047,9 @@ void lookupReplacedDecl(DeclName replacedDeclName,
20472047
declCtxt = storage->getDeclContext();
20482048
}
20492049

2050+
auto *moduleScopeCtxt = declCtxt->getModuleScopeContext();
20502051
if (isa<FileUnit>(declCtxt)) {
2051-
UnqualifiedLookup lookup(replacedDeclName,
2052-
replacement->getModuleScopeContext(), nullptr,
2052+
UnqualifiedLookup lookup(replacedDeclName, moduleScopeCtxt, nullptr,
20532053
attr->getLocation());
20542054
if (lookup.isSuccess()) {
20552055
for (auto entry : lookup.Results) {
@@ -2064,8 +2064,8 @@ void lookupReplacedDecl(DeclName replacedDeclName,
20642064
if (!typeCtx)
20652065
typeCtx = cast<ExtensionDecl>(declCtxt->getAsDecl())->getExtendedNominal();
20662066

2067-
replacement->getModuleScopeContext()->lookupQualified(
2068-
{typeCtx}, replacedDeclName, NL_QualifiedDefault, results);
2067+
moduleScopeCtxt->lookupQualified({typeCtx}, replacedDeclName,
2068+
NL_QualifiedDefault, results);
20692069
}
20702070

20712071
/// Remove any argument labels from the interface type of the given value that
@@ -2183,51 +2183,107 @@ static FuncDecl *findReplacedAccessor(DeclName replacedVarName,
21832183

21842184
static AbstractFunctionDecl *
21852185
findReplacedFunction(DeclName replacedFunctionName,
2186-
AbstractFunctionDecl *replacement,
2187-
DynamicReplacementAttr *attr, TypeChecker &TC) {
2186+
const AbstractFunctionDecl *replacement,
2187+
DynamicReplacementAttr *attr, TypeChecker *TC) {
21882188

2189+
// Note: we might pass a constant attribute when typechecker is nullptr.
2190+
// Any modification to attr must be guarded by a null check on TC.
2191+
//
21892192
SmallVector<ValueDecl *, 4> results;
21902193
lookupReplacedDecl(replacedFunctionName, attr, replacement, results);
21912194

21922195
for (auto *result : results) {
21932196
// Check for static/instance mismatch.
21942197
if (result->isStatic() != replacement->isStatic())
21952198
continue;
2196-
2197-
TC.validateDecl(result);
2199+
if (TC)
2200+
TC->validateDecl(result);
21982201
if (result->getInterfaceType()->getCanonicalType()->matches(
21992202
replacement->getInterfaceType()->getCanonicalType(),
22002203
TypeMatchFlags::AllowABICompatible)) {
22012204
if (!result->isDynamic()) {
2202-
TC.diagnose(attr->getLocation(),
2203-
diag::dynamic_replacement_function_not_dynamic,
2204-
replacedFunctionName);
2205-
attr->setInvalid();
2205+
if (TC) {
2206+
TC->diagnose(attr->getLocation(),
2207+
diag::dynamic_replacement_function_not_dynamic,
2208+
replacedFunctionName);
2209+
attr->setInvalid();
2210+
}
22062211
return nullptr;
22072212
}
22082213
return cast<AbstractFunctionDecl>(result);
22092214
}
22102215
}
2211-
if (results.empty())
2212-
TC.diagnose(attr->getLocation(),
2213-
diag::dynamic_replacement_function_not_found,
2214-
attr->getReplacedFunctionName());
2215-
else {
2216-
TC.diagnose(attr->getLocation(),
2217-
diag::dynamic_replacement_function_of_type_not_found,
2218-
attr->getReplacedFunctionName(),
2219-
replacement->getInterfaceType()->getCanonicalType());
2216+
2217+
if (!TC)
2218+
return nullptr;
2219+
2220+
if (results.empty()) {
2221+
TC->diagnose(attr->getLocation(),
2222+
diag::dynamic_replacement_function_not_found,
2223+
attr->getReplacedFunctionName());
2224+
} else {
2225+
TC->diagnose(attr->getLocation(),
2226+
diag::dynamic_replacement_function_of_type_not_found,
2227+
attr->getReplacedFunctionName(),
2228+
replacement->getInterfaceType()->getCanonicalType());
22202229

22212230
for (auto *result : results) {
2222-
TC.diagnose(SourceLoc(), diag::dynamic_replacement_found_function_of_type,
2223-
attr->getReplacedFunctionName(),
2224-
result->getInterfaceType()->getCanonicalType());
2231+
TC->diagnose(SourceLoc(),
2232+
diag::dynamic_replacement_found_function_of_type,
2233+
attr->getReplacedFunctionName(),
2234+
result->getInterfaceType()->getCanonicalType());
22252235
}
22262236
}
22272237
attr->setInvalid();
22282238
return nullptr;
22292239
}
22302240

2241+
static AbstractStorageDecl *
2242+
findReplacedStorageDecl(DeclName replacedFunctionName,
2243+
const AbstractStorageDecl *replacement,
2244+
const DynamicReplacementAttr *attr) {
2245+
2246+
SmallVector<ValueDecl *, 4> results;
2247+
lookupReplacedDecl(replacedFunctionName, attr, replacement, results);
2248+
2249+
for (auto *result : results) {
2250+
// Check for static/instance mismatch.
2251+
if (result->isStatic() != replacement->isStatic())
2252+
continue;
2253+
if (result->getInterfaceType()->getCanonicalType()->matches(
2254+
replacement->getInterfaceType()->getCanonicalType(),
2255+
TypeMatchFlags::AllowABICompatible)) {
2256+
if (!result->isDynamic()) {
2257+
return nullptr;
2258+
}
2259+
return cast<AbstractStorageDecl>(result);
2260+
}
2261+
}
2262+
return nullptr;
2263+
}
2264+
2265+
ValueDecl *TypeChecker::findReplacedDynamicFunction(const ValueDecl *vd) {
2266+
assert(isa<AbstractFunctionDecl>(vd) || isa<AbstractStorageDecl>(vd));
2267+
if (isa<AccessorDecl>(vd))
2268+
return nullptr;
2269+
2270+
auto *attr = vd->getAttrs().getAttribute<DynamicReplacementAttr>();
2271+
if (!attr)
2272+
return nullptr;
2273+
2274+
auto *afd = dyn_cast<AbstractFunctionDecl>(vd);
2275+
if (afd) {
2276+
// When we pass nullptr as the type checker argument attr is truely const.
2277+
return findReplacedFunction(attr->getReplacedFunctionName(), afd,
2278+
const_cast<DynamicReplacementAttr *>(attr),
2279+
nullptr);
2280+
}
2281+
auto *storageDecl = dyn_cast<AbstractStorageDecl>(vd);
2282+
if (!storageDecl)
2283+
return nullptr;
2284+
return findReplacedStorageDecl(attr->getReplacedFunctionName(), storageDecl, attr);
2285+
}
2286+
22312287
void TypeChecker::checkDynamicReplacementAttribute(ValueDecl *D) {
22322288
assert(isa<AbstractFunctionDecl>(D) || isa<AbstractStorageDecl>(D));
22332289

@@ -2276,7 +2332,7 @@ void TypeChecker::checkDynamicReplacementAttribute(ValueDecl *D) {
22762332
// Otherwise, find the matching function.
22772333
auto *fun = cast<AbstractFunctionDecl>(D);
22782334
if (auto *orig = findReplacedFunction(attr->getReplacedFunctionName(), fun,
2279-
attr, *this)) {
2335+
attr, this)) {
22802336
origs.push_back(orig);
22812337
replacements.push_back(fun);
22822338
} else

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,6 +1048,20 @@ Optional<ObjCReason> shouldMarkAsObjC(const ValueDecl *VD, bool allowImplicit) {
10481048
return shouldMarkClassAsObjC(classDecl);
10491049
}
10501050

1051+
// Infer @objc for @_dynamicReplacement(for:) when replaced decl is @objc.
1052+
if (isa<AbstractFunctionDecl>(VD) || isa<AbstractStorageDecl>(VD))
1053+
if (auto *replacementAttr =
1054+
VD->getAttrs().getAttribute<DynamicReplacementAttr>()) {
1055+
if (auto *replaced = replacementAttr->getReplacedFunction()) {
1056+
if (replaced->isObjC())
1057+
return ObjCReason(ObjCReason::ImplicitlyObjC);
1058+
} else if (auto *replaced =
1059+
TypeChecker::findReplacedDynamicFunction(VD)) {
1060+
if (replaced->isObjC())
1061+
return ObjCReason(ObjCReason::ImplicitlyObjC);
1062+
}
1063+
}
1064+
10511065
// Destructors are always @objc, with -dealloc as their entry point.
10521066
if (isa<DestructorDecl>(VD))
10531067
return ObjCReason(ObjCReason::ImplicitlyObjC);

lib/Sema/TypeChecker.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,6 +1099,7 @@ class TypeChecker final : public LazyResolver {
10991099
static void addImplicitDynamicAttribute(Decl *D);
11001100
void checkDeclAttributes(Decl *D);
11011101
void checkDynamicReplacementAttribute(ValueDecl *D);
1102+
static ValueDecl *findReplacedDynamicFunction(const ValueDecl *d);
11021103
void checkTypeModifyingDeclAttributes(VarDecl *var);
11031104

11041105
void checkReferenceOwnershipAttr(VarDecl *D, ReferenceOwnershipAttr *attr);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import Foundation
2+
3+
@objc protocol Proto {
4+
func implicitObjCMethod()
5+
}
6+
7+
class Object : Proto {
8+
func implicitObjCMethod() {}
9+
10+
@objc dynamic
11+
func objCMethod() {}
12+
13+
@objc dynamic
14+
var objcProperty : Int {
15+
return 0
16+
}
17+
@objc dynamic
18+
var objcProperty2 : Int = 0
19+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -I %t -module-name SomeModule -emit-module -emit-module-path=%t/SomeModule.swiftmodule %S/Inputs/objc_dynamic_replacement.swift -enable-private-imports -swift-version 5 -enable-implicit-dynamic
3+
// RUN: %target-swift-emit-silgen -I %t -enable-sil-ownership %s -swift-version 5 | %FileCheck %s
4+
5+
// REQUIRES: objc_interop
6+
7+
import Foundation
8+
@_private(sourceFile: "Foo.swift") import SomeModule
9+
10+
extension Object {
11+
// CHECK-DAG: sil hidden [ossa] @$s10SomeModule6ObjectC24objc_dynamic_replacementE0F0yyF
12+
// CHECK-DAG: sil hidden [thunk] [objc_replacement_for "implicitObjCMethod"] [ossa] @$s10SomeModule6ObjectC24objc_dynamic_replacementE0F0yyFTo
13+
@_dynamicReplacement(for: implicitObjCMethod())
14+
func replacement() {
15+
}
16+
17+
// CHECK-DAG: sil hidden [ossa] @$s10SomeModule6ObjectC24objc_dynamic_replacementE12replacement2yyF
18+
// CHECK-DAG: sil hidden [thunk] [objc_replacement_for "objCMethod"] [ossa] @$s10SomeModule6ObjectC24objc_dynamic_replacementE12replacement2yyFTo
19+
@_dynamicReplacement(for: objCMethod())
20+
func replacement2() {
21+
}
22+
23+
// CHECK-DAG: sil hidden [thunk] [objc_replacement_for "objcProperty"] [ossa] @$s10SomeModule6ObjectC24objc_dynamic_replacementE12replacement3SivgTo
24+
// CHECK-DAG: sil hidden [ossa] @$s10SomeModule6ObjectC24objc_dynamic_replacementE12replacement3Sivg
25+
@_dynamicReplacement(for: objcProperty)
26+
var replacement3 : Int {
27+
return 2
28+
}
29+
30+
// CHECK-DAG: sil hidden [thunk] [objc_replacement_for "objcProperty2"] [ossa] @$s10SomeModule6ObjectC24objc_dynamic_replacementE12replacement4SivgTo
31+
// CHECK-DAG: sil hidden [ossa] @$s10SomeModule6ObjectC24objc_dynamic_replacementE12replacement4Sivg
32+
@_dynamicReplacement(for: objcProperty2)
33+
var replacement4 : Int {
34+
get {
35+
return 2
36+
}
37+
set {
38+
}
39+
}
40+
}

test/attr/attr_objc_dynamic_replacement.swift

Lines changed: 0 additions & 22 deletions
This file was deleted.

0 commit comments

Comments
 (0)