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 // 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 // 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); Impl.setSinglePCHImport(None);
if (!cachedContents.empty() && cachedContents.back() == '\0') if (!cachedContents.empty() && cachedContents.back() == '\0')
@@ -3702,8 +3702,16 @@ EffectiveClangContext ClangImporter::Implementation::getEffectiveClangContext(
(nominal->getAttrs().hasAttribute<ObjCAttr>() || (nominal->getAttrs().hasAttribute<ObjCAttr>() ||
(!nominal->getParentSourceFile() && nominal->isObjC()))) { (!nominal->getParentSourceFile() && nominal->isObjC()))) {
// Map the name. If we can't represent the Swift name in Clang. // Map the name. If we can't represent the Swift name in Clang.
// FIXME: We should be using the Objective-C name here! Identifier name = nominal->getName();
auto clangName = exportName(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) if (!clangName)
return EffectiveClangContext(); return EffectiveClangContext();

View File

@@ -4535,24 +4535,64 @@ namespace {
} }
template <typename T, typename U> 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 = const auto &languageVersion =
Impl.SwiftContext.LangOpts.EffectiveLanguageVersion; 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; SmallVector<ValueDecl *, 4> results;
overlay->lookupValue(name, NLKind::QualifiedLookup, results); overlay->lookupValue(name, NLKind::QualifiedLookup, results);
T *found = nullptr; T *found = nullptr;
for (auto result : results) { for (auto result : results) {
if (auto singleResult = dyn_cast<T>(result)) { if (auto singleResult = dyn_cast<T>(result)) {
// Skip versioned variants. if (isMatch(singleResult, /*baseNameMatches=*/true)) {
const DeclAttributes &attrs = singleResult->getAttrs(); if (found)
if (attrs.isUnavailableInSwiftVersion(languageVersion)) return nullptr;
continue; found = singleResult;
}
}
}
if (found) if (!found && !hasKnownSwiftName) {
return nullptr; // Try harder to find a match looking at just custom Objective-C names.
SmallVector<Decl *, 64> results;
found = singleResult; 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> template <typename T, typename U>
T *resolveSwiftDecl(const U *decl, Identifier name, T *resolveSwiftDecl(const U *decl, Identifier name,
ClangModuleUnit *clangModule) { bool hasKnownSwiftName, ClangModuleUnit *clangModule) {
if (auto overlay = clangModule->getOverlayModule()) if (auto overlay = clangModule->getOverlayModule())
return resolveSwiftDeclImpl<T>(decl, name, overlay); return resolveSwiftDeclImpl<T>(decl, name, hasKnownSwiftName, overlay);
if (clangModule == Impl.ImportedHeaderUnit) { if (clangModule == Impl.ImportedHeaderUnit) {
// Use an index-based loop because new owners can come in as we're // Use an index-based loop because new owners can come in as we're
// iterating. // iterating.
for (size_t i = 0; i < Impl.ImportedHeaderOwners.size(); ++i) { for (size_t i = 0; i < Impl.ImportedHeaderOwners.size(); ++i) {
ModuleDecl *owner = Impl.ImportedHeaderOwners[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; return result;
} }
} }
@@ -4586,7 +4627,8 @@ namespace {
if (!importer::hasNativeSwiftDecl(decl)) if (!importer::hasNativeSwiftDecl(decl))
return false; return false;
auto wrapperUnit = cast<ClangModuleUnit>(dc->getModuleScopeContext()); auto wrapperUnit = cast<ClangModuleUnit>(dc->getModuleScopeContext());
swiftDecl = resolveSwiftDecl<T>(decl, name, wrapperUnit); swiftDecl = resolveSwiftDecl<T>(decl, name, /*hasCustomSwiftName=*/true,
wrapperUnit);
return true; return true;
} }
@@ -4615,6 +4657,7 @@ namespace {
*correctSwiftName); *correctSwiftName);
Identifier name = importedName.getDeclName().getBaseIdentifier(); Identifier name = importedName.getDeclName().getBaseIdentifier();
bool hasKnownSwiftName = importedName.hasCustomName();
// FIXME: Figure out how to deal with incomplete protocols, since that // FIXME: Figure out how to deal with incomplete protocols, since that
// notion doesn't exist in Swift. // notion doesn't exist in Swift.
@@ -4622,6 +4665,7 @@ namespace {
// Check if this protocol is implemented in its overlay. // Check if this protocol is implemented in its overlay.
if (auto clangModule = Impl.getClangModuleForDecl(decl, true)) if (auto clangModule = Impl.getClangModuleForDecl(decl, true))
if (auto native = resolveSwiftDecl<ProtocolDecl>(decl, name, if (auto native = resolveSwiftDecl<ProtocolDecl>(decl, name,
hasKnownSwiftName,
clangModule)) clangModule))
return native; return native;
@@ -4733,11 +4777,13 @@ namespace {
*correctSwiftName); *correctSwiftName);
auto name = importedName.getDeclName().getBaseIdentifier(); auto name = importedName.getDeclName().getBaseIdentifier();
bool hasKnownSwiftName = importedName.hasCustomName();
if (!decl->hasDefinition()) { if (!decl->hasDefinition()) {
// Check if this class is implemented in its overlay. // Check if this class is implemented in its overlay.
if (auto clangModule = Impl.getClangModuleForDecl(decl, true)) { if (auto clangModule = Impl.getClangModuleForDecl(decl, true)) {
if (auto native = resolveSwiftDecl<ClassDecl>(decl, name, if (auto native = resolveSwiftDecl<ClassDecl>(decl, name,
hasKnownSwiftName,
clangModule)) { clangModule)) {
return native; return native;
} }

View File

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

View File

@@ -1393,7 +1393,7 @@ IsObjCRequest::evaluate(Evaluator &evaluator, ValueDecl *VD) const {
// Classes can be @objc. // Classes can be @objc.
// Protocols and enums can also be @objc, but this is covered by the // 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); isObjC = shouldMarkAsObjC(VD, /*allowImplicit=*/false);
} else if (auto enumDecl = dyn_cast<EnumDecl>(VD)) { } else if (auto enumDecl = dyn_cast<EnumDecl>(VD)) {
// Enums can be @objc so long as they have a raw type that is representable // 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 id<SwiftProtoWithCustomName> _Nonnull
convertToProto(SwiftClassWithCustomName *_Nonnull obj); 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") SWIFT_CLASS("BOGUS")
@interface BogusClass @interface BogusClass
@end @end

View File

@@ -31,3 +31,70 @@ public class CustomNameClass : CustomName {
@objc func protoMethod() @objc func protoMethod()
@objc var protoProperty: Int { get } @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.protoMethod()
_ = obj.protoProperty _ = 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'}}