Skip to content

Commit 93006cc

Browse files
committed
Begin checking cdecl implementations
This commit diagnoses cdecl implementations with no matching imported declaration, and also runs them through the ObjCImplementationChecker. Actually testing that the ObjCImplementationChecker diagnoses various failure conditions correctly will be added in a subsequent commit.
1 parent df18ae5 commit 93006cc

File tree

9 files changed

+142
-32
lines changed

9 files changed

+142
-32
lines changed

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,9 @@ WARNING(api_pattern_attr_ignored, none,
134134

135135
ERROR(objc_implementation_two_impls, none,
136136
"duplicate implementation of Objective-C %select{|category %0 on }0"
137-
"class %1",
138-
(Identifier, ValueDecl *))
137+
"%kind1",
138+
(Identifier, Decl *))
139+
139140
NOTE(previous_objc_implementation, none,
140141
"previously implemented by extension here", ())
141142

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1729,6 +1729,10 @@ ERROR(attr_objc_implementation_category_not_found,none,
17291729
"could not find category %0 on Objective-C class %1; make sure your "
17301730
"umbrella or bridging header imports the header that declares it",
17311731
(Identifier, ValueDecl*))
1732+
ERROR(attr_objc_implementation_func_not_found,none,
1733+
"could not find imported function '%0' matching %kind1; make sure your "
1734+
"umbrella or bridging header imports the header that declares it",
1735+
(StringRef, ValueDecl*))
17321736
NOTE(attr_objc_implementation_fixit_remove_category_name,none,
17331737
"remove arguments to implement the main '@interface' for this class",
17341738
())

include/swift/AST/TypeCheckRequests.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4463,7 +4463,7 @@ class SemanticBriefCommentRequest
44634463
/// This is done on all of a class's implementations at once to improve diagnostics.
44644464
class TypeCheckObjCImplementationRequest
44654465
: public SimpleRequest<TypeCheckObjCImplementationRequest,
4466-
evaluator::SideEffect(ExtensionDecl *),
4466+
evaluator::SideEffect(Decl *),
44674467
RequestFlags::Cached> {
44684468
public:
44694469
using SimpleRequest::SimpleRequest;
@@ -4473,7 +4473,7 @@ class TypeCheckObjCImplementationRequest
44734473

44744474
// Evaluation.
44754475
evaluator::SideEffect
4476-
evaluate(Evaluator &evaluator, ExtensionDecl *ED) const;
4476+
evaluate(Evaluator &evaluator, Decl *D) const;
44774477

44784478
public:
44794479
// Separate caching.

lib/ClangImporter/ClangImporter.cpp

Lines changed: 85 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5783,28 +5783,100 @@ findContextInterfaceAndImplementation(DeclContext *dc) {
57835783
return constructResult(interfaceDecl, implDecls, classDecl, categoryName);
57845784
}
57855785

5786+
static void lookupRelatedFuncs(AbstractFunctionDecl *func,
5787+
SmallVectorImpl<ValueDecl *> &results) {
5788+
DeclName swiftName;
5789+
if (auto accessor = dyn_cast<AccessorDecl>(func))
5790+
swiftName = accessor->getStorage()->getName();
5791+
else
5792+
swiftName = func->getName();
5793+
5794+
if (auto ty = func->getDeclContext()->getSelfNominalTypeDecl()) {
5795+
ty->lookupQualified({ ty }, DeclNameRef(swiftName), func->getLoc(),
5796+
NL_QualifiedDefault | NL_IgnoreAccessControl, results);
5797+
}
5798+
else {
5799+
auto mod = func->getDeclContext()->getParentModule();
5800+
mod->lookupQualified(mod, DeclNameRef(swiftName), func->getLoc(),
5801+
NL_RemoveOverridden | NL_IgnoreAccessControl, results);
5802+
}
5803+
}
5804+
5805+
static ObjCInterfaceAndImplementation
5806+
findFunctionInterfaceAndImplementation(AbstractFunctionDecl *func) {
5807+
if (!func)
5808+
return {};
5809+
5810+
// If this isn't either a clang import or an implementation, there's no point
5811+
// doing any work here.
5812+
if (!func->hasClangNode() && !func->isObjCImplementation())
5813+
return {};
5814+
5815+
OptionalEnum<AccessorKind> accessorKind;
5816+
if (auto accessor = dyn_cast<AccessorDecl>(func))
5817+
accessorKind = accessor->getAccessorKind();
5818+
5819+
StringRef clangName = func->getCDeclName();
5820+
if (clangName.empty())
5821+
return {};
5822+
5823+
SmallVector<ValueDecl *, 4> results;
5824+
lookupRelatedFuncs(func, results);
5825+
5826+
// Classify the `results` as either the interface or an implementation.
5827+
// (Multiple implementations are invalid but utterable.)
5828+
Decl *interface = nullptr;
5829+
TinyPtrVector<Decl *> impls;
5830+
5831+
for (ValueDecl *result : results) {
5832+
AbstractFunctionDecl *resultFunc = nullptr;
5833+
if (accessorKind) {
5834+
if (auto resultStorage = dyn_cast<AbstractStorageDecl>(result))
5835+
resultFunc = resultStorage->getAccessor(*accessorKind);
5836+
}
5837+
else
5838+
resultFunc = dyn_cast<AbstractFunctionDecl>(result);
5839+
5840+
if (!resultFunc)
5841+
continue;
5842+
5843+
if (resultFunc->getCDeclName() != clangName)
5844+
continue;
5845+
5846+
if (resultFunc->hasClangNode()) {
5847+
if (interface) {
5848+
// This clang name is overloaded. That should only happen with C++
5849+
// functions/methods, which aren't currently supported.
5850+
return {};
5851+
}
5852+
interface = result;
5853+
} else if (resultFunc->isObjCImplementation()) {
5854+
impls.push_back(result);
5855+
}
5856+
}
5857+
5858+
// If we found enough decls to construct a result, `func` should be among them
5859+
// somewhere.
5860+
assert(interface == nullptr || impls.empty() ||
5861+
interface == func || llvm::is_contained(impls, func));
5862+
5863+
return constructResult(interface, impls, interface,
5864+
/*categoryName=*/Identifier());
5865+
}
5866+
57865867
ObjCInterfaceAndImplementation ObjCInterfaceAndImplementationRequest::
57875868
evaluate(Evaluator &evaluator, Decl *decl) const {
5788-
// These have direct links to their counterparts through the
5869+
// Types and extensions have direct links to their counterparts through the
57895870
// `@_objcImplementation` attribute. Let's resolve that.
57905871
// (Also directing nulls here, where they'll early-return.)
57915872
if (auto ty = dyn_cast_or_null<NominalTypeDecl>(decl))
57925873
return findContextInterfaceAndImplementation(ty);
57935874
else if (auto ext = dyn_cast<ExtensionDecl>(decl))
57945875
return findContextInterfaceAndImplementation(ext);
5876+
// Abstract functions have to be matched through their @_cdecl attributes.
5877+
else if (auto func = dyn_cast<AbstractFunctionDecl>(decl))
5878+
return findFunctionInterfaceAndImplementation(func);
57955879

5796-
// Anything else is resolved by first locating the context's interface and
5797-
// impl, then matching it to its counterpart. (Instead of calling
5798-
// `findContextInterfaceAndImplementation()` directly, we'll use the request
5799-
// recursively to take advantage of caching.)
5800-
auto contextDecl = decl->getDeclContext()->getAsDecl();
5801-
if (!contextDecl)
5802-
return {};
5803-
5804-
ObjCInterfaceAndImplementationRequest req(contextDecl);
5805-
/*auto contextPair =*/ evaluateOrDefault(evaluator, req, {});
5806-
5807-
// TODO: Implement member matching.
58085880
return {};
58095881
}
58105882

lib/Sema/TypeCheckAttr.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1554,6 +1554,14 @@ visitObjCImplementationAttr(ObjCImplementationAttr *attr) {
15541554

15551555
attr->setCategoryNameInvalid();
15561556
}
1557+
1558+
// FIXME: if (AFD->getCDeclName().empty())
1559+
1560+
if (!AFD->getImplementedObjCDecl()) {
1561+
diagnose(attr->getLocation(),
1562+
diag::attr_objc_implementation_func_not_found,
1563+
AFD->getCDeclName(), AFD);
1564+
}
15571565
}
15581566
}
15591567

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2775,12 +2775,12 @@ bool swift::diagnoseObjCUnsatisfiedOptReqConflicts(SourceFile &sf) {
27752775
return anyDiagnosed;
27762776
}
27772777

2778-
void TypeChecker::checkObjCImplementation(ExtensionDecl *ED) {
2779-
if (!ED->getImplementedObjCDecl())
2778+
void TypeChecker::checkObjCImplementation(Decl *D) {
2779+
if (!D->getImplementedObjCDecl())
27802780
return;
27812781

2782-
evaluateOrDefault(ED->getASTContext().evaluator,
2783-
TypeCheckObjCImplementationRequest{ED},
2782+
evaluateOrDefault(D->getASTContext().evaluator,
2783+
TypeCheckObjCImplementationRequest{D},
27842784
evaluator::SideEffect());
27852785
}
27862786

@@ -2901,10 +2901,20 @@ class ObjCImplementationChecker {
29012901
llvm::SmallDenseMap<ValueDecl *, ObjCSelector, 16> unmatchedCandidates;
29022902

29032903
public:
2904-
ObjCImplementationChecker(ExtensionDecl *ext)
2905-
: diags(ext->getASTContext().Diags)
2904+
ObjCImplementationChecker(Decl *D)
2905+
: diags(D->getASTContext().Diags)
29062906
{
2907-
assert(!ext->hasClangNode() && "passed interface, not impl, to checker");
2907+
assert(!D->hasClangNode() && "passed interface, not impl, to checker");
2908+
2909+
if (auto func = dyn_cast<AbstractFunctionDecl>(D)) {
2910+
addCandidate(D);
2911+
addRequirement(D->getImplementedObjCDecl());
2912+
2913+
return;
2914+
}
2915+
2916+
// Otherwise this must be an extension.
2917+
auto ext = cast<ExtensionDecl>(D);
29082918

29092919
// Conformances are declared exclusively in the interface, so diagnose any
29102920
// in the implementation right away.
@@ -3083,12 +3093,20 @@ class ObjCImplementationChecker {
30833093
}
30843094

30853095
static ObjCSelector getExplicitObjCName(ValueDecl *VD) {
3096+
if (auto cdeclAttr = VD->getAttrs().getAttribute<CDeclAttr>()) {
3097+
auto ident = VD->getASTContext().getIdentifier(cdeclAttr->Name);
3098+
return ObjCSelector(VD->getASTContext(), 0, { ident });
3099+
}
30863100
if (auto objcAttr = VD->getAttrs().getAttribute<ObjCAttr>())
30873101
return objcAttr->getName().value_or(ObjCSelector());
30883102
return ObjCSelector();
30893103
}
30903104

30913105
static llvm::Optional<ObjCSelector> getObjCName(ValueDecl *VD) {
3106+
if (!VD->getCDeclName().empty()) {
3107+
auto ident = VD->getASTContext().getIdentifier(VD->getCDeclName());
3108+
return ObjCSelector(VD->getASTContext(), 0, { ident });
3109+
}
30923110
return VD->getObjCRuntimeName();
30933111
}
30943112

@@ -3401,8 +3419,11 @@ class ObjCImplementationChecker {
34013419
if (req->isInstanceMember() != cand->isInstanceMember())
34023420
return MatchOutcome::WrongStaticness;
34033421

3404-
if (cand->getDeclContext()->getImplementedObjCContext()
3405-
!= req->getDeclContext())
3422+
// Check only applies to members of implementations, not implementations in
3423+
// their own right.
3424+
if (!cand->isObjCImplementation()
3425+
&& cand->getDeclContext()->getImplementedObjCContext()
3426+
!= req->getDeclContext())
34063427
return MatchOutcome::WrongCategory;
34073428

34083429
if (cand->getKind() != req->getKind())
@@ -3648,8 +3669,8 @@ class ObjCImplementationChecker {
36483669
}
36493670

36503671
evaluator::SideEffect TypeCheckObjCImplementationRequest::
3651-
evaluate(Evaluator &evaluator, ExtensionDecl *ED) const {
3652-
PrettyStackTraceDecl trace("checking member implementations of", ED);
3672+
evaluate(Evaluator &evaluator, Decl *D) const {
3673+
PrettyStackTraceDecl trace("checking member implementations of", D);
36533674

36543675
// FIXME: Because we check extension-by-extension, candidates and requirements
36553676
// from different extensions are never compared, so we never get an
@@ -3658,7 +3679,7 @@ evaluate(Evaluator &evaluator, ExtensionDecl *ED) const {
36583679
// candidates we considered to all unmatched requirements in the module, and
36593680
// vice versa. The tricky bit is making sure we only diagnose for candidates
36603681
// and requirements in our primary files!
3661-
ObjCImplementationChecker checker(ED);
3682+
ObjCImplementationChecker checker(D);
36623683

36633684
checker.matchRequirements();
36643685
checker.diagnoseUnmatchedCandidates();

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3603,6 +3603,8 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
36033603
reason.setAttrInvalid();
36043604
}
36053605
}
3606+
3607+
TypeChecker::checkObjCImplementation(FD);
36063608
}
36073609

36083610
void visitModuleDecl(ModuleDecl *) { }
@@ -4045,6 +4047,8 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
40454047

40464048
checkDefaultArguments(CD->getParameters());
40474049
checkVariadicParameters(CD->getParameters(), CD);
4050+
4051+
TypeChecker::checkObjCImplementation(CD);
40484052
}
40494053

40504054
void visitDestructorDecl(DestructorDecl *DD) {

lib/Sema/TypeChecker.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -657,8 +657,8 @@ void checkDeclCircularity(NominalTypeDecl *decl);
657657
/// Type check whether an extension matches its Objective-C interface, if it
658658
/// has one.
659659
///
660-
/// \param ED The extension to check.
661-
void checkObjCImplementation(ExtensionDecl *ED);
660+
/// \param D The declaration to check.
661+
void checkObjCImplementation(Decl *D);
662662

663663
/// Type check whether the given switch statement exhaustively covers
664664
/// its domain.

test/decl/ext/objc_implementation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ func CImplFunc2(_: Int32) {
476476

477477
@_objcImplementation @_cdecl("CImplFuncMissing")
478478
func CImplFuncMissing(_: Int32) {
479-
// FIXME: expected-DISABLED-error@-1 {{fnord}}
479+
// expected-error@-2 {{could not find imported function 'CImplFuncMissing' matching global function 'CImplFuncMissing'; make sure your umbrella or bridging header imports the header that declares it}}
480480
}
481481

482482
func usesAreNotAmbiguous(obj: ObjCClass) {

0 commit comments

Comments
 (0)