Skip to content

[cxx-interop] Fix assertion failure from unavailable typedef + enum pattern #81625

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 6 commits into from
May 22, 2025
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
55 changes: 6 additions & 49 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -847,31 +847,6 @@ static bool isPrintLikeMethod(DeclName name, const DeclContext *dc) {
using MirroredMethodEntry =
std::tuple<const clang::ObjCMethodDecl*, ProtocolDecl*, bool /*isAsync*/>;

ImportedType findOptionSetType(clang::QualType type,
ClangImporter::Implementation &Impl) {
ImportedType importedType;
auto fieldType = type;
if (auto elaborated = dyn_cast<clang::ElaboratedType>(fieldType))
fieldType = elaborated->desugar();
if (auto typedefType = dyn_cast<clang::TypedefType>(fieldType)) {
if (Impl.isUnavailableInSwift(typedefType->getDecl())) {
if (auto clangEnum =
findAnonymousEnumForTypedef(Impl.SwiftContext, typedefType)) {
// If this fails, it means that we need a stronger predicate for
// determining the relationship between an enum and typedef.
assert(
clangEnum.value()->getIntegerType()->getCanonicalTypeInternal() ==
typedefType->getCanonicalTypeInternal());
if (auto swiftEnum = Impl.importDecl(*clangEnum, Impl.CurrentVersion)) {
importedType = {cast<TypeDecl>(swiftEnum)->getDeclaredInterfaceType(),
false};
}
}
}
}
return importedType;
}

static bool areRecordFieldsComplete(const clang::CXXRecordDecl *decl) {
for (const auto *f : decl->fields()) {
auto *fieldRecord = f->getType()->getAsCXXRecordDecl();
Expand Down Expand Up @@ -4511,8 +4486,8 @@ namespace {
return nullptr;
}

auto fieldType = decl->getType();
ImportedType importedType = findOptionSetType(fieldType, Impl);
auto fieldType = desugarIfElaborated(decl->getType());
ImportedType importedType = importer::findOptionSetEnum(fieldType, Impl);

if (!importedType)
importedType =
Expand Down Expand Up @@ -6155,8 +6130,8 @@ namespace {
}
}

auto fieldType = decl->getType();
ImportedType importedType = findOptionSetType(fieldType, Impl);
auto fieldType = desugarIfElaborated(decl->getType());
ImportedType importedType = importer::findOptionSetEnum(fieldType, Impl);

if (!importedType)
importedType = Impl.importPropertyType(decl, isInSystemModule(dc));
Expand Down Expand Up @@ -7120,8 +7095,7 @@ SwiftDeclConverter::getImplicitProperty(ImportedName importedName,
getAccessorPropertyType(getter, false, getterName.getSelfIndex());
if (propertyType.isNull())
return nullptr;
if (auto elaborated = dyn_cast<clang::ElaboratedType>(propertyType))
propertyType = elaborated->desugar();
propertyType = desugarIfElaborated(propertyType);

// If there is a setter, check that the property it implies
// matches that of the getter.
Expand All @@ -7147,24 +7121,7 @@ SwiftDeclConverter::getImplicitProperty(ImportedName importedName,
if (dc->isTypeContext() && !getterName.getSelfIndex())
isStatic = true;

ImportedType importedType;

// 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>(propertyType.getTypePtr())) {
if (Impl.isUnavailableInSwift(typedefType->getDecl())) {
if (auto clangEnum = findAnonymousEnumForTypedef(Impl.SwiftContext, typedefType)) {
// If this fails, it means that we need a stronger predicate for
// determining the relationship between an enum and typedef.
assert(clangEnum.value()->getIntegerType()->getCanonicalTypeInternal() ==
typedefType->getCanonicalTypeInternal());
if (auto swiftEnum = Impl.importDecl(*clangEnum, Impl.CurrentVersion)) {
importedType = {cast<TypeDecl>(swiftEnum)->getDeclaredInterfaceType(),
false};
}
}
}
}
ImportedType importedType = importer::findOptionSetEnum(propertyType, Impl);

if (!importedType) {
// Compute the property type.
Expand Down
42 changes: 38 additions & 4 deletions lib/ClangImporter/ImportEnumInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,44 @@ StringRef importer::getCommonPluralPrefix(StringRef singular,
}

const clang::Type *importer::getUnderlyingType(const clang::EnumDecl *decl) {
const clang::Type *underlyingType = decl->getIntegerType().getTypePtr();
if (auto elaborated = dyn_cast<clang::ElaboratedType>(underlyingType))
underlyingType = elaborated->desugar().getTypePtr();
return underlyingType;
return importer::desugarIfElaborated(decl->getIntegerType().getTypePtr());
}

ImportedType importer::findOptionSetEnum(clang::QualType type,
ClangImporter::Implementation &Impl) {
auto typedefType = dyn_cast<clang::TypedefType>(type);
if (!typedefType || !Impl.isUnavailableInSwift(typedefType->getDecl()))
// If this isn't a typedef, or it is a typedef that is available in Swift,
// then this definitely isn't used for {CF,NS}_OPTIONS.
return ImportedType();

if (Impl.SwiftContext.LangOpts.EnableCXXInterop &&
!isCFOptionsMacro(typedefType->getDecl(), Impl.getClangPreprocessor())) {
return ImportedType();
}

auto clangEnum = findAnonymousEnumForTypedef(Impl.SwiftContext, typedefType);
if (!clangEnum)
return ImportedType();

// Assert that the typedef has the same underlying integer representation as
// the enum we think it assigns a type name to.
//
// If these fails, it means that we need a stronger predicate for
// determining the relationship between an enum and typedef.
if (auto *tdEnum =
dyn_cast<clang::EnumType>(typedefType->getCanonicalTypeInternal())) {
ASSERT(clangEnum.value()->getIntegerType()->getCanonicalTypeInternal() ==
tdEnum->getDecl()->getIntegerType()->getCanonicalTypeInternal());
} else {
ASSERT(clangEnum.value()->getIntegerType()->getCanonicalTypeInternal() ==
typedefType->getCanonicalTypeInternal());
}

if (auto *swiftEnum = Impl.importDecl(*clangEnum, Impl.CurrentVersion))
return {cast<TypeDecl>(swiftEnum)->getDeclaredInterfaceType(), false};

return ImportedType();
}

/// Determine the prefix to be stripped from the names of the enum constants
Expand Down
14 changes: 10 additions & 4 deletions lib/ClangImporter/ImportEnumInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

#include "swift/AST/ASTContext.h"
#include "swift/AST/Decl.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/Sema.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/DenseMap.h"

Expand Down Expand Up @@ -164,14 +166,18 @@ StringRef getCommonPluralPrefix(StringRef singular, StringRef plural);
/// an elaborated type, an unwrapped type is returned.
const clang::Type *getUnderlyingType(const clang::EnumDecl *decl);

inline bool isCFOptionsMacro(StringRef macroName) {
return llvm::StringSwitch<bool>(macroName)
inline bool isCFOptionsMacro(const clang::NamedDecl *decl,
clang::Preprocessor &preprocessor) {
auto loc = decl->getEndLoc();
if (!loc.isMacroID())
return false;
return llvm::StringSwitch<bool>(preprocessor.getImmediateMacroName(loc))
.Case("CF_OPTIONS", true)
.Case("NS_OPTIONS", true)
.Default(false);
}

}
}
} // namespace importer
} // namespace swift

#endif // SWIFT_CLANG_IMPORT_ENUM_H
13 changes: 4 additions & 9 deletions lib/ClangImporter/ImportName.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "CFTypeInfo.h"
#include "ClangClassTemplateNamePrinter.h"
#include "ClangDiagnosticConsumer.h"
#include "ImportEnumInfo.h"
#include "ImporterImpl.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/ClangSwiftTypeCorrespondence.h"
Expand Down Expand Up @@ -1877,15 +1878,9 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
// imported into Swift to avoid having two types with the same name, which
// cause subtle name lookup issues.
if (swiftCtx.LangOpts.EnableCXXInterop &&
isUnavailableInSwift(D, nullptr, true)) {
auto loc = D->getEndLoc();
if (loc.isMacroID()) {
StringRef macroName =
clangSema.getPreprocessor().getImmediateMacroName(loc);
if (isCFOptionsMacro(macroName))
return ImportedName();
}
}
isUnavailableInSwift(D, nullptr, true) &&
isCFOptionsMacro(D, clangSema.getPreprocessor()))
return ImportedName();

/// Whether the result is a function name.
bool isFunction = false;
Expand Down
109 changes: 19 additions & 90 deletions lib/ClangImporter/ImportType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
//===----------------------------------------------------------------------===//

#include "CFTypeInfo.h"
#include "ImportEnumInfo.h"
#include "ImporterImpl.h"
#include "SwiftDeclSynthesizer.h"
#include "swift/ABI/MetadataValues.h"
Expand Down Expand Up @@ -2282,10 +2283,7 @@ ImportedType ClangImporter::Implementation::importFunctionReturnType(
OptionalityOfReturn = OTK_ImplicitlyUnwrappedOptional;
}

clang::QualType returnType = clangDecl->getReturnType();
if (auto elaborated =
dyn_cast<clang::ElaboratedType>(returnType))
returnType = elaborated->desugar();
clang::QualType returnType = desugarIfElaborated(clangDecl->getReturnType());
// In C interop mode, the return type of library builtin functions
// like 'memcpy' from headers like 'string.h' drops
// any nullability specifiers from their return type, and preserves it on the
Expand Down Expand Up @@ -2323,19 +2321,9 @@ ImportedType ClangImporter::Implementation::importFunctionReturnType(
->isTemplateTypeParmType())
OptionalityOfReturn = OTK_None;

if (auto typedefType = dyn_cast<clang::TypedefType>(returnType)) {
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.value()->getIntegerType()->getCanonicalTypeInternal() ==
typedefType->getCanonicalTypeInternal());
if (auto swiftEnum = importDecl(*clangEnum, CurrentVersion)) {
return {cast<TypeDecl>(swiftEnum)->getDeclaredInterfaceType(), false};
}
}
}
}
ImportedType optionSetEnum = importer::findOptionSetEnum(returnType, *this);
if (optionSetEnum)
return optionSetEnum;

// Import the underlying result type.
if (clangDecl) {
Expand Down Expand Up @@ -2399,27 +2387,11 @@ ImportedType ClangImporter::Implementation::importFunctionParamsAndReturnType(

// Only eagerly import the return type if it's not too expensive (the current
// heuristic for that is if it's not a record type).
ImportedType importedType;
ImportDiagnosticAdder addDiag(*this, clangDecl,
clangDecl->getSourceRange().getBegin());
clang::QualType returnType = clangDecl->getReturnType();
if (auto elaborated = dyn_cast<clang::ElaboratedType>(returnType))
returnType = elaborated->desugar();

if (auto typedefType = dyn_cast<clang::TypedefType>(returnType)) {
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.value()->getIntegerType()->getCanonicalTypeInternal() ==
typedefType->getCanonicalTypeInternal());
if (auto swiftEnum = importDecl(*clangEnum, CurrentVersion)) {
importedType = {cast<TypeDecl>(swiftEnum)->getDeclaredInterfaceType(),
false};
}
}
}
}
clang::QualType returnType = desugarIfElaborated(clangDecl->getReturnType());

ImportedType importedType = importer::findOptionSetEnum(returnType, *this);

if (auto templateType =
dyn_cast<clang::TemplateTypeParmType>(returnType)) {
Expand Down Expand Up @@ -2483,9 +2455,7 @@ ClangImporter::Implementation::importParameterType(
std::optional<unsigned> completionHandlerErrorParamIndex,
ArrayRef<GenericTypeParamDecl *> genericParams,
llvm::function_ref<void(Diagnostic &&)> addImportDiagnosticFn) {
auto paramTy = param->getType();
if (auto elaborated = dyn_cast<clang::ElaboratedType>(paramTy))
paramTy = elaborated->desugar();
auto paramTy = desugarIfElaborated(param->getType());

ImportTypeKind importKind = paramIsCompletionHandler
? ImportTypeKind::CompletionHandlerParameter
Expand All @@ -2498,23 +2468,8 @@ ClangImporter::Implementation::importParameterType(
bool isConsuming = false;
bool isParamTypeImplicitlyUnwrapped = false;

// 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.value()
->getIntegerType()
->getCanonicalTypeInternal() ==
typedefType->getCanonicalTypeInternal());
if (auto swiftEnum = importDecl(*clangEnum, CurrentVersion)) {
swiftParamTy = cast<TypeDecl>(swiftEnum)->getDeclaredInterfaceType();
}
}
}
if (auto optionSetEnum = importer::findOptionSetEnum(paramTy, *this)) {
swiftParamTy = optionSetEnum.getType();
} else if (isa<clang::PointerType>(paramTy) &&
isa<clang::TemplateTypeParmType>(paramTy->getPointeeType())) {
auto pointeeType = paramTy->getPointeeType();
Expand Down Expand Up @@ -2955,13 +2910,8 @@ ArgumentAttrs ClangImporter::Implementation::inferDefaultArgument(
return argumentAttrs;
}
}
auto loc = typedefDecl->getEndLoc();
if (loc.isMacroID()) {
StringRef macroName =
nameImporter.getClangPreprocessor().getImmediateMacroName(loc);
if (isCFOptionsMacro(macroName))
return argumentAttrs;
}
if (isCFOptionsMacro(typedefDecl, nameImporter.getClangPreprocessor()))
return argumentAttrs;
}
}

Expand Down Expand Up @@ -3234,26 +3184,8 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType(

ImportDiagnosticAdder addImportDiag(*this, clangDecl,
clangDecl->getLocation());
clang::QualType resultType = clangDecl->getReturnType();
if (auto elaborated = dyn_cast<clang::ElaboratedType>(resultType))
resultType = elaborated->desugar();

ImportedType importedType;
if (auto typedefType = dyn_cast<clang::TypedefType>(resultType.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.value()->getIntegerType()->getCanonicalTypeInternal() ==
typedefType->getCanonicalTypeInternal());
if (auto swiftEnum = importDecl(*clangEnum, CurrentVersion)) {
importedType = {cast<TypeDecl>(swiftEnum)->getDeclaredInterfaceType(),
false};
}
}
}
}

clang::QualType resultType = desugarIfElaborated(clangDecl->getReturnType());
ImportedType importedType = importer::findOptionSetEnum(resultType, *this);
if (!importedType)
importedType = importType(resultType, resultKind, addImportDiag,
allowNSUIntegerAsIntInResult, Bridgeability::Full,
Expand Down Expand Up @@ -3501,9 +3433,6 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType(
importedType.isImplicitlyUnwrapped()};
}

ImportedType findOptionSetType(clang::QualType type,
ClangImporter::Implementation &Impl);

ImportedType ClangImporter::Implementation::importAccessorParamsAndReturnType(
const DeclContext *dc, const clang::ObjCPropertyDecl *property,
const clang::ObjCMethodDecl *clangDecl, bool isFromSystemModule,
Expand All @@ -3529,11 +3458,11 @@ ImportedType ClangImporter::Implementation::importAccessorParamsAndReturnType(
if (!origDC)
return {Type(), false};

auto fieldType = isGetter ? clangDecl->getReturnType()
: clangDecl->getParamDecl(0)->getType();

auto fieldType =
desugarIfElaborated(isGetter ? clangDecl->getReturnType()
: clangDecl->getParamDecl(0)->getType());
// Import the property type, independent of what kind of accessor this is.
ImportedType importedType = findOptionSetType(fieldType, *this);
ImportedType importedType = importer::findOptionSetEnum(fieldType, *this);
if (!importedType)
importedType = importPropertyType(property, isFromSystemModule);
if (!importedType)
Expand Down
Loading