Skip to content

[cxx-interop] Import enum, not typedef for parameter types. #42431

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 52 additions & 18 deletions lib/ClangImporter/ImportType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2125,6 +2125,20 @@ ImportedType ClangImporter::Implementation::importFunctionParamsAndReturnType(
ImportedType importedType;
ImportDiagnosticAdder addDiag(*this, clangDecl,
clangDecl->getSourceRange().getBegin());
if (auto typedefType = dyn_cast<clang::TypedefType>(clangDecl->getReturnType().getTypePtr())) {
if (isUnavailableInSwift(typedefType->getDecl())) {
if (auto clangEnum = findAnonymousEnumForTypedef(SwiftContext, typedefType)) {
// If this fails, it means that we need a stronger predicate for
// determining the relationship between an enum and typedef.
assert(clangEnum.getValue()->getIntegerType()->getCanonicalTypeInternal() ==
typedefType->getCanonicalTypeInternal());
if (auto swiftEnum = importDecl(*clangEnum, CurrentVersion)) {
importedType = {cast<NominalTypeDecl>(swiftEnum)->getDeclaredType(), false};
}
}
}
}

if (auto templateType =
dyn_cast<clang::TemplateTypeParmType>(clangDecl->getReturnType())) {
importedType = {findGenericTypeInGenericDecls(
Expand All @@ -2150,11 +2164,15 @@ ImportedType ClangImporter::Implementation::importFunctionParamsAndReturnType(
clangDecl->isOverloadedOperator() ||
// Dependant types are trivially mapped as Any.
clangDecl->getReturnType()->isDependentType()) {
importedType =
importFunctionReturnType(dc, clangDecl, allowNSUIntegerAsInt);
// If importedType is already initialized, it means we found the enum that
// was supposed to be used (instead of the typedef type).
if (!importedType) {
addDiag(Diagnostic(diag::return_type_not_imported));
return {Type(), false};
importedType =
importFunctionReturnType(dc, clangDecl, allowNSUIntegerAsInt);
if (!importedType) {
addDiag(Diagnostic(diag::return_type_not_imported));
return {Type(), false};
}
}
}

Expand Down Expand Up @@ -2238,7 +2256,22 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList(
Type swiftParamTy;
bool isParamTypeImplicitlyUnwrapped = false;
bool isInOut = false;
if (isa<clang::PointerType>(paramTy) &&

// Sometimes we import unavailable typedefs as enums. If that's the case,
// use the enum, not the typedef here.
if (auto typedefType = dyn_cast<clang::TypedefType>(paramTy.getTypePtr())) {
if (isUnavailableInSwift(typedefType->getDecl())) {
if (auto clangEnum = findAnonymousEnumForTypedef(SwiftContext, typedefType)) {
// If this fails, it means that we need a stronger predicate for
// determining the relationship between an enum and typedef.
assert(clangEnum.getValue()->getIntegerType()->getCanonicalTypeInternal() ==
typedefType->getCanonicalTypeInternal());
if (auto swiftEnum = importDecl(*clangEnum, CurrentVersion)) {
swiftParamTy = cast<NominalTypeDecl>(swiftEnum)->getDeclaredType();
}
}
}
} else if (isa<clang::PointerType>(paramTy) &&
isa<clang::TemplateTypeParmType>(paramTy->getPointeeType())) {
auto pointeeType = paramTy->getPointeeType();
auto templateParamType = cast<clang::TemplateTypeParmType>(pointeeType);
Expand All @@ -2265,20 +2298,21 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList(
swiftParamTy =
findGenericTypeInGenericDecls(*this, templateParamType, genericParams,
attrs, paramAddDiag);
} else {
if (auto refType = dyn_cast<clang::ReferenceType>(paramTy)) {
// We don't support reference type to a dependent type, just bail.
if (refType->getPointeeType()->isDependentType()) {
addImportDiagnostic(
param, Diagnostic(diag::parameter_type_not_imported, param),
param->getSourceRange().getBegin());
return nullptr;
}

paramTy = refType->getPointeeType();
if (!paramTy.isConstQualified())
isInOut = true;
} else if (auto refType = dyn_cast<clang::ReferenceType>(paramTy)) {
// We don't support reference type to a dependent type, just bail.
if (refType->getPointeeType()->isDependentType()) {
addImportDiagnostic(
param, Diagnostic(diag::parameter_type_not_imported, param),
param->getSourceRange().getBegin());
return nullptr;
}

paramTy = refType->getPointeeType();
if (!paramTy.isConstQualified())
isInOut = true;
}

if (!swiftParamTy) {
auto importedType = importType(paramTy, importKind, paramAddDiag,
allowNSUIntegerAsInt, Bridgeability::Full,
attrs, OptionalityOfParam);
Expand Down
27 changes: 23 additions & 4 deletions lib/ClangImporter/ImporterImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
#include "swift/ClangImporter/ClangImporter.h"
#include "swift/ClangImporter/ClangModule.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/Lex/MacroInfo.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/AST/DeclVisitor.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/IdentifierTable.h"
Expand Down Expand Up @@ -60,12 +63,8 @@ class SmallBitVector;

namespace clang {
class APValue;
class Decl;
class DeclarationName;
class EnumDecl;
class MacroInfo;
class MangleContext;
class NamedDecl;
class ObjCInterfaceDecl;
class ObjCMethodDecl;
class ObjCPropertyDecl;
Expand Down Expand Up @@ -1862,6 +1861,26 @@ static inline Type applyToFunctionType(
return type;
}

inline Optional<const clang::EnumDecl *> findAnonymousEnumForTypedef(
const ASTContext &ctx,
const clang::TypedefType *typedefType) {
auto *typedefDecl = typedefType->getDecl();
auto *lookupTable = ctx.getClangModuleLoader()->findLookupTable(typedefDecl->getOwningModule());

auto foundDecls = lookupTable->lookup(
SerializedSwiftName(typedefDecl->getName()), EffectiveClangContext());

auto found = llvm::find_if(foundDecls, [](SwiftLookupTable::SingleEntry decl) {
return decl.is<clang::NamedDecl *>() &&
isa<clang::EnumDecl>(decl.get<clang::NamedDecl *>());
});

if (found != foundDecls.end())
return cast<clang::EnumDecl>(found->get<clang::NamedDecl *>());

return None;
}

}
}

Expand Down
3 changes: 3 additions & 0 deletions test/Interop/Cxx/enum/Inputs/anonymous-with-swift-name.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@ typedef CF_OPTIONS(unsigned, CFColorMask) {
kCFColorMaskAll = ~0U
};

inline SOColorMask useSOColorMask(SOColorMask mask) { return mask; }
inline CFColorMask useCFColorMask(CFColorMask mask) { return mask; }

#endif // TEST_INTEROP_CXX_ENUM_INPUTS_ANONYMOUS_WITH_SWIFT_NAME_H
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,6 @@
// CHECK: @available(swift, obsoleted: 3, renamed: "all")
// CHECK: static var All: CFColorMask { get }
// CHECK: }

// CHECK: func useSOColorMask(_ mask: SOColorMask) -> SOColorMask
// CHECK: func useCFColorMask(_ mask: CFColorMask) -> CFColorMask
21 changes: 17 additions & 4 deletions test/Interop/Cxx/enum/anonymous-with-swift-name.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,28 @@ AnonymousEnumsTestSuite.test("SOME_OPTIONS") {
}

AnonymousEnumsTestSuite.test("CF_OPTIONS") {
let red: SOColorMask = .red
let green = SOColorMask.green
let blue = .blue as SOColorMask
let all: SOColorMask = .all
let red: CFColorMask = .red
let green = CFColorMask.green
let blue = .blue as CFColorMask
let all: CFColorMask = .all

expectEqual(red.rawValue, 2)
expectEqual(green.rawValue, 4)
expectEqual(blue.rawValue, 8)
expectEqual(all.rawValue, ~CUnsignedInt(0))
}

AnonymousEnumsTestSuite.test("Parameter types") {
let red: CFColorMask = .red
let green = CFColorMask.green

let blue = useSOColorMask(.blue)
let all = useSOColorMask(.all)

expectEqual(red, useCFColorMask(.red))
expectEqual(green, useCFColorMask(.green))
expectEqual(blue, .blue)
expectEqual(all, .all)
}

runAllTests()