Merge pull request #27682 from ChristopherRogers/master

[ClangImporter] Fix edge cases where custom name matches native name
This commit is contained in:
Jordan Rose
2019-10-23 13:51:17 -07:00
committed by GitHub
7 changed files with 227 additions and 20 deletions

View File

@@ -1354,7 +1354,7 @@ bool ClangImporter::importHeader(StringRef header, ModuleDecl *adapter,
// If we've made it to here, this is some header other than the bridging
// header, which means we can no longer rely on one file's modification time
// to invalid code completion caches. :-(
// to invalidate code completion caches. :-(
Impl.setSinglePCHImport(None);
if (!cachedContents.empty() && cachedContents.back() == '\0')
@@ -3702,8 +3702,16 @@ EffectiveClangContext ClangImporter::Implementation::getEffectiveClangContext(
(nominal->getAttrs().hasAttribute<ObjCAttr>() ||
(!nominal->getParentSourceFile() && nominal->isObjC()))) {
// Map the name. If we can't represent the Swift name in Clang.
// FIXME: We should be using the Objective-C name here!
auto clangName = exportName(nominal->getName());
Identifier name = nominal->getName();
if (auto objcAttr = nominal->getAttrs().getAttribute<ObjCAttr>()) {
if (auto objcName = objcAttr->getName()) {
if (objcName->getNumArgs() == 0) {
// This is an error if not 0, but it should be caught later.
name = objcName->getSimpleName();
}
}
}
auto clangName = exportName(name);
if (!clangName)
return EffectiveClangContext();

View File

@@ -4535,24 +4535,64 @@ namespace {
}
template <typename T, typename U>
T *resolveSwiftDeclImpl(const U *decl, Identifier name, ModuleDecl *overlay) {
T *resolveSwiftDeclImpl(const U *decl, Identifier name,
bool hasKnownSwiftName, ModuleDecl *overlay) {
const auto &languageVersion =
Impl.SwiftContext.LangOpts.EffectiveLanguageVersion;
auto isMatch = [&](const T *singleResult, bool baseNameMatches) -> bool {
const DeclAttributes &attrs = singleResult->getAttrs();
// Skip versioned variants.
if (attrs.isUnavailableInSwiftVersion(languageVersion))
return false;
// Skip if type not exposed to Objective-C.
// If the base name doesn't match, then a matching
// custom name in an @objc attribute is required.
if (baseNameMatches && !singleResult->isObjC())
return false;
// If Clang decl has a custom Swift name, then we know that
// `name` is the base name we're looking for.
if (hasKnownSwiftName)
return baseNameMatches;
// Skip if a different name is used for Objective-C.
if (auto objcAttr = attrs.getAttribute<ObjCAttr>())
if (auto objcName = objcAttr->getName())
return objcName->getSimpleName() == name;
return baseNameMatches;
};
// First look at Swift types with the same name.
SmallVector<ValueDecl *, 4> results;
overlay->lookupValue(name, NLKind::QualifiedLookup, results);
T *found = nullptr;
for (auto result : results) {
if (auto singleResult = dyn_cast<T>(result)) {
// Skip versioned variants.
const DeclAttributes &attrs = singleResult->getAttrs();
if (attrs.isUnavailableInSwiftVersion(languageVersion))
continue;
if (isMatch(singleResult, /*baseNameMatches=*/true)) {
if (found)
return nullptr;
found = singleResult;
}
}
}
if (found)
return nullptr;
found = singleResult;
if (!found && !hasKnownSwiftName) {
// Try harder to find a match looking at just custom Objective-C names.
SmallVector<Decl *, 64> results;
overlay->getTopLevelDecls(results);
for (auto result : results) {
if (auto singleResult = dyn_cast<T>(result)) {
// The base name _could_ match but it's irrelevant here.
if (isMatch(singleResult, /*baseNameMatches=*/false)) {
if (found)
return nullptr;
found = singleResult;
}
}
}
}
@@ -4565,15 +4605,16 @@ namespace {
template <typename T, typename U>
T *resolveSwiftDecl(const U *decl, Identifier name,
ClangModuleUnit *clangModule) {
bool hasKnownSwiftName, ClangModuleUnit *clangModule) {
if (auto overlay = clangModule->getOverlayModule())
return resolveSwiftDeclImpl<T>(decl, name, overlay);
return resolveSwiftDeclImpl<T>(decl, name, hasKnownSwiftName, overlay);
if (clangModule == Impl.ImportedHeaderUnit) {
// Use an index-based loop because new owners can come in as we're
// iterating.
for (size_t i = 0; i < Impl.ImportedHeaderOwners.size(); ++i) {
ModuleDecl *owner = Impl.ImportedHeaderOwners[i];
if (T *result = resolveSwiftDeclImpl<T>(decl, name, owner))
if (T *result = resolveSwiftDeclImpl<T>(decl, name,
hasKnownSwiftName, owner))
return result;
}
}
@@ -4586,7 +4627,8 @@ namespace {
if (!importer::hasNativeSwiftDecl(decl))
return false;
auto wrapperUnit = cast<ClangModuleUnit>(dc->getModuleScopeContext());
swiftDecl = resolveSwiftDecl<T>(decl, name, wrapperUnit);
swiftDecl = resolveSwiftDecl<T>(decl, name, /*hasCustomSwiftName=*/true,
wrapperUnit);
return true;
}
@@ -4615,6 +4657,7 @@ namespace {
*correctSwiftName);
Identifier name = importedName.getDeclName().getBaseIdentifier();
bool hasKnownSwiftName = importedName.hasCustomName();
// FIXME: Figure out how to deal with incomplete protocols, since that
// notion doesn't exist in Swift.
@@ -4622,6 +4665,7 @@ namespace {
// Check if this protocol is implemented in its overlay.
if (auto clangModule = Impl.getClangModuleForDecl(decl, true))
if (auto native = resolveSwiftDecl<ProtocolDecl>(decl, name,
hasKnownSwiftName,
clangModule))
return native;
@@ -4733,11 +4777,13 @@ namespace {
*correctSwiftName);
auto name = importedName.getDeclName().getBaseIdentifier();
bool hasKnownSwiftName = importedName.hasCustomName();
if (!decl->hasDefinition()) {
// Check if this class is implemented in its overlay.
if (auto clangModule = Impl.getClangModuleForDecl(decl, true)) {
if (auto native = resolveSwiftDecl<ClassDecl>(decl, name,
hasKnownSwiftName,
clangModule)) {
return native;
}

View File

@@ -1781,9 +1781,11 @@ ImportedName NameImporter::importName(const clang::NamedDecl *decl,
ImportNameVersion version,
clang::DeclarationName givenName) {
CacheKeyType key(decl, version);
if (importNameCache.count(key) && !givenName) {
++ImportNameNumCacheHits;
return importNameCache[key];
if (!givenName) {
if (auto cachedRes = importNameCache[key]) {
++ImportNameNumCacheHits;
return cachedRes;
}
}
++ImportNameNumCacheMisses;
auto res = importNameImpl(decl, version, givenName);

View File

@@ -1393,7 +1393,7 @@ IsObjCRequest::evaluate(Evaluator &evaluator, ValueDecl *VD) const {
// Classes can be @objc.
// Protocols and enums can also be @objc, but this is covered by the
// isObjC() check a the beginning.;
// isObjC() check at the beginning.
isObjC = shouldMarkAsObjC(VD, /*allowImplicit=*/false);
} else if (auto enumDecl = dyn_cast<EnumDecl>(VD)) {
// Enums can be @objc so long as they have a raw type that is representable

View File

@@ -68,6 +68,62 @@ SWIFT_PROTOCOL("SwiftProto")
id<SwiftProtoWithCustomName> _Nonnull
convertToProto(SwiftClassWithCustomName *_Nonnull obj);
SWIFT_CLASS("ObjCClass")
__attribute__((objc_root_class))
@interface ObjCClass
@end
SWIFT_CLASS("ImplicitlyObjCClass")
@interface ImplicitlyObjCClass : ObjCClass
@end
void consumeImplicitlyObjCClass(ImplicitlyObjCClass *_Nonnull obj);
SWIFT_CLASS("ExplicitlyObjCClass")
__attribute__((objc_root_class))
@interface ExplicitlyObjCClass
@end
void consumeExplicitlyObjCClass(ExplicitlyObjCClass *_Nonnull obj);
SWIFT_CLASS_NAMED("HasSameCustomNameClass")
__attribute__((objc_root_class))
@interface HasSameCustomNameClass
@end
void consumeHasSameCustomNameClass(HasSameCustomNameClass *_Nonnull obj);
SWIFT_CLASS_NAMED("SwiftNativeTypeHasDifferentCustomNameClass")
__attribute__((objc_root_class))
@interface NativeTypeHasDifferentCustomNameClass
@end
SWIFT_CLASS_NAMED("NativeTypeHasDifferentCustomNameClass")
__attribute__((objc_root_class))
@interface ObjCNativeTypeHasDifferentCustomNameClass
@end
void consumeNativeTypeHasDifferentCustomNameClass(NativeTypeHasDifferentCustomNameClass *_Nonnull obj);
void consumeObjCNativeTypeHasDifferentCustomNameClass(ObjCNativeTypeHasDifferentCustomNameClass *_Nonnull obj);
SWIFT_CLASS_NAMED("SwiftNativeTypeIsNonObjCClass")
__attribute__((objc_root_class))
@interface NativeTypeIsNonObjCClass
@end
void consumeNativeTypeIsNonObjCClass(NativeTypeIsNonObjCClass *_Nonnull obj);
@class ForwardImplicitlyObjCClass;
void consumeForwardImplicitlyObjCClass(ForwardImplicitlyObjCClass *_Nonnull obj);
@class ForwardExplicitlyObjCClass;
void consumeForwardExplicitlyObjCClass(ForwardExplicitlyObjCClass *_Nonnull obj);
@class ForwardHasSameCustomNameClass;
void consumeForwardHasSameCustomNameClass(ForwardHasSameCustomNameClass *_Nonnull obj);
@class ForwardNativeTypeHasDifferentCustomNameClass;
@class ObjCForwardNativeTypeHasDifferentCustomNameClass;
void consumeForwardNativeTypeHasDifferentCustomNameClass(ForwardNativeTypeHasDifferentCustomNameClass *_Nonnull obj);
void consumeObjCForwardNativeTypeHasDifferentCustomNameClass(ObjCForwardNativeTypeHasDifferentCustomNameClass *_Nonnull obj);
@class ForwardNativeTypeIsNonObjCClass;
void consumeForwardNativeTypeIsNonObjCClass(ForwardNativeTypeIsNonObjCClass *_Nonnull obj);
SWIFT_CLASS("BOGUS")
@interface BogusClass
@end

View File

@@ -31,3 +31,70 @@ public class CustomNameClass : CustomName {
@objc func protoMethod()
@objc var protoProperty: Int { get }
}
@objc
public class ObjCClass {
public init() {}
}
public class ImplicitlyObjCClass : ObjCClass {
public override init() { super.init() }
}
@objc
public class ExplicitlyObjCClass {
public init() {}
}
@objc(HasSameCustomNameClass)
public class HasSameCustomNameClass {
public init() {}
}
@objc(ObjCNativeTypeHasDifferentCustomNameClass)
public class NativeTypeHasDifferentCustomNameClass {
public init() {}
}
@objc(NativeTypeHasDifferentCustomNameClass)
public class SwiftNativeTypeHasDifferentCustomNameClass {
public init() {}
}
public class NativeTypeIsNonObjCClass {
public init() {}
}
@objc(NativeTypeIsNonObjCClass)
public class SwiftNativeTypeIsNonObjCClass {
public init() {}
}
public class ForwardImplicitlyObjCClass : ObjCClass {
public override init() { super.init() }
}
@objc
public class ForwardExplicitlyObjCClass {
public init() {}
}
@objc(ForwardHasSameCustomNameClass)
public class ForwardHasSameCustomNameClass {
public init() {}
}
@objc(ObjCForwardNativeTypeHasDifferentCustomNameClass)
public class ForwardNativeTypeHasDifferentCustomNameClass {
public init() {}
}
@objc(ForwardNativeTypeHasDifferentCustomNameClass)
public class SwiftForwardNativeTypeHasDifferentCustomNameClass {
public init() {}
}
public class ForwardNativeTypeIsNonObjCClass {
public init() {}
}
@objc(ForwardNativeTypeIsNonObjCClass)
public class SwiftForwardNativeTypeIsNonObjCClass {
public init() {}
}

View File

@@ -32,3 +32,31 @@ func testAnyObject(_ obj: AnyObject) {
obj.protoMethod()
_ = obj.protoProperty
}
consumeImplicitlyObjCClass(ImplicitlyObjCClass())
consumeExplicitlyObjCClass(ExplicitlyObjCClass())
consumeHasSameCustomNameClass(HasSameCustomNameClass())
consumeNativeTypeHasDifferentCustomNameClass(SwiftNativeTypeHasDifferentCustomNameClass())
consumeObjCNativeTypeHasDifferentCustomNameClass(NativeTypeHasDifferentCustomNameClass())
consumeNativeTypeHasDifferentCustomNameClass(NativeTypeHasDifferentCustomNameClass()) // expected-error {{cannot convert value of type 'NativeTypeHasDifferentCustomNameClass' to expected argument type 'SwiftNativeTypeHasDifferentCustomNameClass'}}
consumeObjCNativeTypeHasDifferentCustomNameClass(SwiftNativeTypeHasDifferentCustomNameClass()) // expected-error {{cannot convert value of type 'SwiftNativeTypeHasDifferentCustomNameClass' to expected argument type 'NativeTypeHasDifferentCustomNameClass'}}
consumeNativeTypeIsNonObjCClass(SwiftNativeTypeIsNonObjCClass())
consumeNativeTypeIsNonObjCClass(NativeTypeIsNonObjCClass()) // expected-error {{cannot convert value of type 'NativeTypeIsNonObjCClass' to expected argument type 'SwiftNativeTypeIsNonObjCClass'}}
consumeForwardImplicitlyObjCClass(ForwardImplicitlyObjCClass())
consumeForwardExplicitlyObjCClass(ForwardExplicitlyObjCClass())
consumeForwardHasSameCustomNameClass(ForwardHasSameCustomNameClass())
consumeForwardNativeTypeHasDifferentCustomNameClass(SwiftForwardNativeTypeHasDifferentCustomNameClass())
consumeObjCForwardNativeTypeHasDifferentCustomNameClass(ForwardNativeTypeHasDifferentCustomNameClass())
consumeForwardNativeTypeHasDifferentCustomNameClass(ForwardNativeTypeHasDifferentCustomNameClass()) // expected-error {{cannot convert value of type 'ForwardNativeTypeHasDifferentCustomNameClass' to expected argument type 'SwiftForwardNativeTypeHasDifferentCustomNameClass'}}
consumeObjCForwardNativeTypeHasDifferentCustomNameClass(SwiftForwardNativeTypeHasDifferentCustomNameClass()) // expected-error {{cannot convert value of type 'SwiftForwardNativeTypeHasDifferentCustomNameClass' to expected argument type 'ForwardNativeTypeHasDifferentCustomNameClass'}}
consumeForwardNativeTypeIsNonObjCClass(SwiftForwardNativeTypeIsNonObjCClass())
consumeForwardNativeTypeIsNonObjCClass(ForwardNativeTypeIsNonObjCClass()) // expected-error {{cannot convert value of type 'ForwardNativeTypeIsNonObjCClass' to expected argument type 'SwiftForwardNativeTypeIsNonObjCClass'}}