Skip to content

Commit 78a9e45

Browse files
Merge pull request swiftlang#22811 from aschwaighofer/infer_objc_dynamic_replacement
Sema: Infer @objc for dynamic replacement
2 parents 860bb79 + 33b8ea3 commit 78a9e45

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
@@ -1977,8 +1977,8 @@ void AttributeChecker::visitDiscardableResultAttr(DiscardableResultAttr *attr) {
19771977

19781978
/// Lookup the replaced decl in the replacments scope.
19791979
void lookupReplacedDecl(DeclName replacedDeclName,
1980-
DynamicReplacementAttr *attr,
1981-
AbstractFunctionDecl *replacement,
1980+
const DynamicReplacementAttr *attr,
1981+
const ValueDecl *replacement,
19821982
SmallVectorImpl<ValueDecl *> &results) {
19831983
auto *declCtxt = replacement->getDeclContext();
19841984

@@ -1988,9 +1988,9 @@ void lookupReplacedDecl(DeclName replacedDeclName,
19881988
declCtxt = storage->getDeclContext();
19891989
}
19901990

1991+
auto *moduleScopeCtxt = declCtxt->getModuleScopeContext();
19911992
if (isa<FileUnit>(declCtxt)) {
1992-
UnqualifiedLookup lookup(replacedDeclName,
1993-
replacement->getModuleScopeContext(), nullptr,
1993+
UnqualifiedLookup lookup(replacedDeclName, moduleScopeCtxt, nullptr,
19941994
attr->getLocation());
19951995
if (lookup.isSuccess()) {
19961996
for (auto entry : lookup.Results) {
@@ -2005,8 +2005,8 @@ void lookupReplacedDecl(DeclName replacedDeclName,
20052005
if (!typeCtx)
20062006
typeCtx = cast<ExtensionDecl>(declCtxt->getAsDecl())->getExtendedNominal();
20072007

2008-
replacement->getModuleScopeContext()->lookupQualified(
2009-
{typeCtx}, replacedDeclName, NL_QualifiedDefault, results);
2008+
moduleScopeCtxt->lookupQualified({typeCtx}, replacedDeclName,
2009+
NL_QualifiedDefault, results);
20102010
}
20112011

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

21252125
static AbstractFunctionDecl *
21262126
findReplacedFunction(DeclName replacedFunctionName,
2127-
AbstractFunctionDecl *replacement,
2128-
DynamicReplacementAttr *attr, TypeChecker &TC) {
2127+
const AbstractFunctionDecl *replacement,
2128+
DynamicReplacementAttr *attr, TypeChecker *TC) {
21292129

2130+
// Note: we might pass a constant attribute when typechecker is nullptr.
2131+
// Any modification to attr must be guarded by a null check on TC.
2132+
//
21302133
SmallVector<ValueDecl *, 4> results;
21312134
lookupReplacedDecl(replacedFunctionName, attr, replacement, results);
21322135

21332136
for (auto *result : results) {
21342137
// Check for static/instance mismatch.
21352138
if (result->isStatic() != replacement->isStatic())
21362139
continue;
2137-
2138-
TC.validateDecl(result);
2140+
if (TC)
2141+
TC->validateDecl(result);
21392142
if (result->getInterfaceType()->getCanonicalType()->matches(
21402143
replacement->getInterfaceType()->getCanonicalType(),
21412144
TypeMatchFlags::AllowABICompatible)) {
21422145
if (!result->isDynamic()) {
2143-
TC.diagnose(attr->getLocation(),
2144-
diag::dynamic_replacement_function_not_dynamic,
2145-
replacedFunctionName);
2146-
attr->setInvalid();
2146+
if (TC) {
2147+
TC->diagnose(attr->getLocation(),
2148+
diag::dynamic_replacement_function_not_dynamic,
2149+
replacedFunctionName);
2150+
attr->setInvalid();
2151+
}
21472152
return nullptr;
21482153
}
21492154
return cast<AbstractFunctionDecl>(result);
21502155
}
21512156
}
2152-
if (results.empty())
2153-
TC.diagnose(attr->getLocation(),
2154-
diag::dynamic_replacement_function_not_found,
2155-
attr->getReplacedFunctionName());
2156-
else {
2157-
TC.diagnose(attr->getLocation(),
2158-
diag::dynamic_replacement_function_of_type_not_found,
2159-
attr->getReplacedFunctionName(),
2160-
replacement->getInterfaceType()->getCanonicalType());
2157+
2158+
if (!TC)
2159+
return nullptr;
2160+
2161+
if (results.empty()) {
2162+
TC->diagnose(attr->getLocation(),
2163+
diag::dynamic_replacement_function_not_found,
2164+
attr->getReplacedFunctionName());
2165+
} else {
2166+
TC->diagnose(attr->getLocation(),
2167+
diag::dynamic_replacement_function_of_type_not_found,
2168+
attr->getReplacedFunctionName(),
2169+
replacement->getInterfaceType()->getCanonicalType());
21612170

21622171
for (auto *result : results) {
2163-
TC.diagnose(SourceLoc(), diag::dynamic_replacement_found_function_of_type,
2164-
attr->getReplacedFunctionName(),
2165-
result->getInterfaceType()->getCanonicalType());
2172+
TC->diagnose(SourceLoc(),
2173+
diag::dynamic_replacement_found_function_of_type,
2174+
attr->getReplacedFunctionName(),
2175+
result->getInterfaceType()->getCanonicalType());
21662176
}
21672177
}
21682178
attr->setInvalid();
21692179
return nullptr;
21702180
}
21712181

2182+
static AbstractStorageDecl *
2183+
findReplacedStorageDecl(DeclName replacedFunctionName,
2184+
const AbstractStorageDecl *replacement,
2185+
const DynamicReplacementAttr *attr) {
2186+
2187+
SmallVector<ValueDecl *, 4> results;
2188+
lookupReplacedDecl(replacedFunctionName, attr, replacement, results);
2189+
2190+
for (auto *result : results) {
2191+
// Check for static/instance mismatch.
2192+
if (result->isStatic() != replacement->isStatic())
2193+
continue;
2194+
if (result->getInterfaceType()->getCanonicalType()->matches(
2195+
replacement->getInterfaceType()->getCanonicalType(),
2196+
TypeMatchFlags::AllowABICompatible)) {
2197+
if (!result->isDynamic()) {
2198+
return nullptr;
2199+
}
2200+
return cast<AbstractStorageDecl>(result);
2201+
}
2202+
}
2203+
return nullptr;
2204+
}
2205+
2206+
ValueDecl *TypeChecker::findReplacedDynamicFunction(const ValueDecl *vd) {
2207+
assert(isa<AbstractFunctionDecl>(vd) || isa<AbstractStorageDecl>(vd));
2208+
if (isa<AccessorDecl>(vd))
2209+
return nullptr;
2210+
2211+
auto *attr = vd->getAttrs().getAttribute<DynamicReplacementAttr>();
2212+
if (!attr)
2213+
return nullptr;
2214+
2215+
auto *afd = dyn_cast<AbstractFunctionDecl>(vd);
2216+
if (afd) {
2217+
// When we pass nullptr as the type checker argument attr is truely const.
2218+
return findReplacedFunction(attr->getReplacedFunctionName(), afd,
2219+
const_cast<DynamicReplacementAttr *>(attr),
2220+
nullptr);
2221+
}
2222+
auto *storageDecl = dyn_cast<AbstractStorageDecl>(vd);
2223+
if (!storageDecl)
2224+
return nullptr;
2225+
return findReplacedStorageDecl(attr->getReplacedFunctionName(), storageDecl, attr);
2226+
}
2227+
21722228
void TypeChecker::checkDynamicReplacementAttribute(ValueDecl *D) {
21732229
assert(isa<AbstractFunctionDecl>(D) || isa<AbstractStorageDecl>(D));
21742230

@@ -2217,7 +2273,7 @@ void TypeChecker::checkDynamicReplacementAttribute(ValueDecl *D) {
22172273
// Otherwise, find the matching function.
22182274
auto *fun = cast<AbstractFunctionDecl>(D);
22192275
if (auto *orig = findReplacedFunction(attr->getReplacedFunctionName(), fun,
2220-
attr, *this)) {
2276+
attr, this)) {
22212277
origs.push_back(orig);
22222278
replacements.push_back(fun);
22232279
} 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)