Skip to content

Commit 1612c6e

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 7e78cb9 commit 1612c6e

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
@@ -1740,6 +1740,10 @@ ERROR(attr_objc_implementation_category_not_found,none,
17401740
"could not find category %0 on Objective-C class %1; make sure your "
17411741
"umbrella or bridging header imports the header that declares it",
17421742
(Identifier, ValueDecl*))
1743+
ERROR(attr_objc_implementation_func_not_found,none,
1744+
"could not find imported function '%0' matching %kind1; make sure your "
1745+
"umbrella or bridging header imports the header that declares it",
1746+
(StringRef, ValueDecl*))
17431747
NOTE(attr_objc_implementation_fixit_remove_category_name,none,
17441748
"remove arguments to implement the main '@interface' for this class",
17451749
())

include/swift/AST/TypeCheckRequests.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4544,7 +4544,7 @@ class SemanticBriefCommentRequest
45444544
/// This is done on all of a class's implementations at once to improve diagnostics.
45454545
class TypeCheckObjCImplementationRequest
45464546
: public SimpleRequest<TypeCheckObjCImplementationRequest,
4547-
evaluator::SideEffect(ExtensionDecl *),
4547+
evaluator::SideEffect(Decl *),
45484548
RequestFlags::Cached> {
45494549
public:
45504550
using SimpleRequest::SimpleRequest;
@@ -4554,7 +4554,7 @@ class TypeCheckObjCImplementationRequest
45544554

45554555
// Evaluation.
45564556
evaluator::SideEffect
4557-
evaluate(Evaluator &evaluator, ExtensionDecl *ED) const;
4557+
evaluate(Evaluator &evaluator, Decl *D) const;
45584558

45594559
public:
45604560
// Separate caching.

lib/ClangImporter/ClangImporter.cpp

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

5931+
static void lookupRelatedFuncs(AbstractFunctionDecl *func,
5932+
SmallVectorImpl<ValueDecl *> &results) {
5933+
DeclName swiftName;
5934+
if (auto accessor = dyn_cast<AccessorDecl>(func))
5935+
swiftName = accessor->getStorage()->getName();
5936+
else
5937+
swiftName = func->getName();
5938+
5939+
if (auto ty = func->getDeclContext()->getSelfNominalTypeDecl()) {
5940+
ty->lookupQualified({ ty }, DeclNameRef(swiftName), func->getLoc(),
5941+
NL_QualifiedDefault | NL_IgnoreAccessControl, results);
5942+
}
5943+
else {
5944+
auto mod = func->getDeclContext()->getParentModule();
5945+
mod->lookupQualified(mod, DeclNameRef(swiftName), func->getLoc(),
5946+
NL_RemoveOverridden | NL_IgnoreAccessControl, results);
5947+
}
5948+
}
5949+
5950+
static ObjCInterfaceAndImplementation
5951+
findFunctionInterfaceAndImplementation(AbstractFunctionDecl *func) {
5952+
if (!func)
5953+
return {};
5954+
5955+
// If this isn't either a clang import or an implementation, there's no point
5956+
// doing any work here.
5957+
if (!func->hasClangNode() && !func->isObjCImplementation())
5958+
return {};
5959+
5960+
OptionalEnum<AccessorKind> accessorKind;
5961+
if (auto accessor = dyn_cast<AccessorDecl>(func))
5962+
accessorKind = accessor->getAccessorKind();
5963+
5964+
StringRef clangName = func->getCDeclName();
5965+
if (clangName.empty())
5966+
return {};
5967+
5968+
SmallVector<ValueDecl *, 4> results;
5969+
lookupRelatedFuncs(func, results);
5970+
5971+
// Classify the `results` as either the interface or an implementation.
5972+
// (Multiple implementations are invalid but utterable.)
5973+
Decl *interface = nullptr;
5974+
TinyPtrVector<Decl *> impls;
5975+
5976+
for (ValueDecl *result : results) {
5977+
AbstractFunctionDecl *resultFunc = nullptr;
5978+
if (accessorKind) {
5979+
if (auto resultStorage = dyn_cast<AbstractStorageDecl>(result))
5980+
resultFunc = resultStorage->getAccessor(*accessorKind);
5981+
}
5982+
else
5983+
resultFunc = dyn_cast<AbstractFunctionDecl>(result);
5984+
5985+
if (!resultFunc)
5986+
continue;
5987+
5988+
if (resultFunc->getCDeclName() != clangName)
5989+
continue;
5990+
5991+
if (resultFunc->hasClangNode()) {
5992+
if (interface) {
5993+
// This clang name is overloaded. That should only happen with C++
5994+
// functions/methods, which aren't currently supported.
5995+
return {};
5996+
}
5997+
interface = result;
5998+
} else if (resultFunc->isObjCImplementation()) {
5999+
impls.push_back(result);
6000+
}
6001+
}
6002+
6003+
// If we found enough decls to construct a result, `func` should be among them
6004+
// somewhere.
6005+
assert(interface == nullptr || impls.empty() ||
6006+
interface == func || llvm::is_contained(impls, func));
6007+
6008+
return constructResult(interface, impls, interface,
6009+
/*categoryName=*/Identifier());
6010+
}
6011+
59316012
ObjCInterfaceAndImplementation ObjCInterfaceAndImplementationRequest::
59326013
evaluate(Evaluator &evaluator, Decl *decl) const {
5933-
// These have direct links to their counterparts through the
6014+
// Types and extensions have direct links to their counterparts through the
59346015
// `@_objcImplementation` attribute. Let's resolve that.
59356016
// (Also directing nulls here, where they'll early-return.)
59366017
if (auto ty = dyn_cast_or_null<NominalTypeDecl>(decl))
59376018
return findContextInterfaceAndImplementation(ty);
59386019
else if (auto ext = dyn_cast<ExtensionDecl>(decl))
59396020
return findContextInterfaceAndImplementation(ext);
6021+
// Abstract functions have to be matched through their @_cdecl attributes.
6022+
else if (auto func = dyn_cast<AbstractFunctionDecl>(decl))
6023+
return findFunctionInterfaceAndImplementation(func);
59406024

5941-
// Anything else is resolved by first locating the context's interface and
5942-
// impl, then matching it to its counterpart. (Instead of calling
5943-
// `findContextInterfaceAndImplementation()` directly, we'll use the request
5944-
// recursively to take advantage of caching.)
5945-
auto contextDecl = decl->getDeclContext()->getAsDecl();
5946-
if (!contextDecl)
5947-
return {};
5948-
5949-
ObjCInterfaceAndImplementationRequest req(contextDecl);
5950-
/*auto contextPair =*/ evaluateOrDefault(evaluator, req, {});
5951-
5952-
// TODO: Implement member matching.
59536025
return {};
59546026
}
59556027

lib/Sema/TypeCheckAttr.cpp

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

15911591
attr->setCategoryNameInvalid();
15921592
}
1593+
1594+
// FIXME: if (AFD->getCDeclName().empty())
1595+
1596+
if (!AFD->getImplementedObjCDecl()) {
1597+
diagnose(attr->getLocation(),
1598+
diag::attr_objc_implementation_func_not_found,
1599+
AFD->getCDeclName(), AFD);
1600+
}
15931601
}
15941602
}
15951603

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
@@ -3567,6 +3567,8 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
35673567
reason.setAttrInvalid();
35683568
}
35693569
}
3570+
3571+
TypeChecker::checkObjCImplementation(FD);
35703572
}
35713573

35723574
void visitModuleDecl(ModuleDecl *) { }
@@ -4006,6 +4008,8 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
40064008

40074009
checkDefaultArguments(CD->getParameters());
40084010
checkVariadicParameters(CD->getParameters(), CD);
4011+
4012+
TypeChecker::checkObjCImplementation(CD);
40094013
}
40104014

40114015
void visitDestructorDecl(DestructorDecl *DD) {

lib/Sema/TypeChecker.h

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

658658
/// Type check whether the given switch statement exhaustively covers
659659
/// 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)