Skip to content

Commit 3db1314

Browse files
authored
Merge pull request #81699 from j-hui/cherry-pick-6.2/fix-unavailable-enum
[6.2 🍒][cxx-interop] Fix assertion failure from unavailable typedef + enum pattern
2 parents 641cf01 + 323e9f0 commit 3db1314

File tree

5 files changed

+140
-136
lines changed

5 files changed

+140
-136
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 6 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -846,31 +846,6 @@ static bool isPrintLikeMethod(DeclName name, const DeclContext *dc) {
846846
using MirroredMethodEntry =
847847
std::tuple<const clang::ObjCMethodDecl*, ProtocolDecl*, bool /*isAsync*/>;
848848

849-
ImportedType findOptionSetType(clang::QualType type,
850-
ClangImporter::Implementation &Impl) {
851-
ImportedType importedType;
852-
auto fieldType = type;
853-
if (auto elaborated = dyn_cast<clang::ElaboratedType>(fieldType))
854-
fieldType = elaborated->desugar();
855-
if (auto typedefType = dyn_cast<clang::TypedefType>(fieldType)) {
856-
if (Impl.isUnavailableInSwift(typedefType->getDecl())) {
857-
if (auto clangEnum =
858-
findAnonymousEnumForTypedef(Impl.SwiftContext, typedefType)) {
859-
// If this fails, it means that we need a stronger predicate for
860-
// determining the relationship between an enum and typedef.
861-
assert(
862-
clangEnum.value()->getIntegerType()->getCanonicalTypeInternal() ==
863-
typedefType->getCanonicalTypeInternal());
864-
if (auto swiftEnum = Impl.importDecl(*clangEnum, Impl.CurrentVersion)) {
865-
importedType = {cast<TypeDecl>(swiftEnum)->getDeclaredInterfaceType(),
866-
false};
867-
}
868-
}
869-
}
870-
}
871-
return importedType;
872-
}
873-
874849
static bool areRecordFieldsComplete(const clang::CXXRecordDecl *decl) {
875850
for (const auto *f : decl->fields()) {
876851
auto *fieldRecord = f->getType()->getAsCXXRecordDecl();
@@ -4540,8 +4515,8 @@ namespace {
45404515
return nullptr;
45414516
}
45424517

4543-
auto fieldType = decl->getType();
4544-
ImportedType importedType = findOptionSetType(fieldType, Impl);
4518+
auto fieldType = desugarIfElaborated(decl->getType());
4519+
ImportedType importedType = importer::findOptionSetEnum(fieldType, Impl);
45454520

45464521
if (!importedType)
45474522
importedType =
@@ -6133,8 +6108,8 @@ namespace {
61336108
}
61346109
}
61356110

6136-
auto fieldType = decl->getType();
6137-
ImportedType importedType = findOptionSetType(fieldType, Impl);
6111+
auto fieldType = desugarIfElaborated(decl->getType());
6112+
ImportedType importedType = importer::findOptionSetEnum(fieldType, Impl);
61386113

61396114
if (!importedType)
61406115
importedType = Impl.importPropertyType(decl, isInSystemModule(dc));
@@ -7098,8 +7073,7 @@ SwiftDeclConverter::getImplicitProperty(ImportedName importedName,
70987073
getAccessorPropertyType(getter, false, getterName.getSelfIndex());
70997074
if (propertyType.isNull())
71007075
return nullptr;
7101-
if (auto elaborated = dyn_cast<clang::ElaboratedType>(propertyType))
7102-
propertyType = elaborated->desugar();
7076+
propertyType = desugarIfElaborated(propertyType);
71037077

71047078
// If there is a setter, check that the property it implies
71057079
// matches that of the getter.
@@ -7125,24 +7099,7 @@ SwiftDeclConverter::getImplicitProperty(ImportedName importedName,
71257099
if (dc->isTypeContext() && !getterName.getSelfIndex())
71267100
isStatic = true;
71277101

7128-
ImportedType importedType;
7129-
7130-
// Sometimes we import unavailable typedefs as enums. If that's the case,
7131-
// use the enum, not the typedef here.
7132-
if (auto typedefType = dyn_cast<clang::TypedefType>(propertyType.getTypePtr())) {
7133-
if (Impl.isUnavailableInSwift(typedefType->getDecl())) {
7134-
if (auto clangEnum = findAnonymousEnumForTypedef(Impl.SwiftContext, typedefType)) {
7135-
// If this fails, it means that we need a stronger predicate for
7136-
// determining the relationship between an enum and typedef.
7137-
assert(clangEnum.value()->getIntegerType()->getCanonicalTypeInternal() ==
7138-
typedefType->getCanonicalTypeInternal());
7139-
if (auto swiftEnum = Impl.importDecl(*clangEnum, Impl.CurrentVersion)) {
7140-
importedType = {cast<TypeDecl>(swiftEnum)->getDeclaredInterfaceType(),
7141-
false};
7142-
}
7143-
}
7144-
}
7145-
}
7102+
ImportedType importedType = importer::findOptionSetEnum(propertyType, Impl);
71467103

71477104
if (!importedType) {
71487105
// Compute the property type.

lib/ClangImporter/ImportEnumInfo.cpp

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -244,10 +244,43 @@ StringRef importer::getCommonPluralPrefix(StringRef singular,
244244
}
245245

246246
const clang::Type *importer::getUnderlyingType(const clang::EnumDecl *decl) {
247-
const clang::Type *underlyingType = decl->getIntegerType().getTypePtr();
248-
if (auto elaborated = dyn_cast<clang::ElaboratedType>(underlyingType))
249-
underlyingType = elaborated->desugar().getTypePtr();
250-
return underlyingType;
247+
return importer::desugarIfElaborated(decl->getIntegerType().getTypePtr());
248+
}
249+
250+
ImportedType importer::findOptionSetEnum(clang::QualType type,
251+
ClangImporter::Implementation &Impl) {
252+
auto typedefType = dyn_cast<clang::TypedefType>(type);
253+
if (!typedefType || !Impl.isUnavailableInSwift(typedefType->getDecl()))
254+
// If this isn't a typedef, or it is a typedef that is available in Swift,
255+
// then this definitely isn't used for {CF,NS}_OPTIONS.
256+
return ImportedType();
257+
258+
auto clangEnum = findAnonymousEnumForTypedef(Impl.SwiftContext, typedefType);
259+
if (!clangEnum)
260+
return ImportedType();
261+
262+
// Only ASSERT() on assertions-enabled builds. This preserves existing
263+
// behavior and de-risks existing builds, but should be removed after 6.2.
264+
#ifndef NDEBUG
265+
// Assert that the typedef has the same underlying integer representation as
266+
// the enum we think it assigns a type name to.
267+
//
268+
// If these fails, it means that we need a stronger predicate for
269+
// determining the relationship between an enum and typedef.
270+
if (auto *tdEnum =
271+
dyn_cast<clang::EnumType>(typedefType->getCanonicalTypeInternal())) {
272+
ASSERT(clangEnum.value()->getIntegerType()->getCanonicalTypeInternal() ==
273+
tdEnum->getDecl()->getIntegerType()->getCanonicalTypeInternal());
274+
} else {
275+
ASSERT(clangEnum.value()->getIntegerType()->getCanonicalTypeInternal() ==
276+
typedefType->getCanonicalTypeInternal());
277+
}
278+
#endif // !NDEBUG
279+
280+
if (auto *swiftEnum = Impl.importDecl(*clangEnum, Impl.CurrentVersion))
281+
return {cast<TypeDecl>(swiftEnum)->getDeclaredInterfaceType(), false};
282+
283+
return ImportedType();
251284
}
252285

253286
/// Determine the prefix to be stripped from the names of the enum constants

lib/ClangImporter/ImportType.cpp

Lines changed: 16 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -2255,10 +2255,7 @@ ImportedType ClangImporter::Implementation::importFunctionReturnType(
22552255
OptionalityOfReturn = OTK_ImplicitlyUnwrappedOptional;
22562256
}
22572257

2258-
clang::QualType returnType = clangDecl->getReturnType();
2259-
if (auto elaborated =
2260-
dyn_cast<clang::ElaboratedType>(returnType))
2261-
returnType = elaborated->desugar();
2258+
clang::QualType returnType = desugarIfElaborated(clangDecl->getReturnType());
22622259
// In C interop mode, the return type of library builtin functions
22632260
// like 'memcpy' from headers like 'string.h' drops
22642261
// any nullability specifiers from their return type, and preserves it on the
@@ -2296,19 +2293,9 @@ ImportedType ClangImporter::Implementation::importFunctionReturnType(
22962293
->isTemplateTypeParmType())
22972294
OptionalityOfReturn = OTK_None;
22982295

2299-
if (auto typedefType = dyn_cast<clang::TypedefType>(returnType)) {
2300-
if (isUnavailableInSwift(typedefType->getDecl())) {
2301-
if (auto clangEnum = findAnonymousEnumForTypedef(SwiftContext, typedefType)) {
2302-
// If this fails, it means that we need a stronger predicate for
2303-
// determining the relationship between an enum and typedef.
2304-
assert(clangEnum.value()->getIntegerType()->getCanonicalTypeInternal() ==
2305-
typedefType->getCanonicalTypeInternal());
2306-
if (auto swiftEnum = importDecl(*clangEnum, CurrentVersion)) {
2307-
return {cast<TypeDecl>(swiftEnum)->getDeclaredInterfaceType(), false};
2308-
}
2309-
}
2310-
}
2311-
}
2296+
ImportedType optionSetEnum = importer::findOptionSetEnum(returnType, *this);
2297+
if (optionSetEnum)
2298+
return optionSetEnum;
23122299

23132300
// Import the underlying result type.
23142301
if (clangDecl) {
@@ -2376,27 +2363,11 @@ ImportedType ClangImporter::Implementation::importFunctionParamsAndReturnType(
23762363

23772364
// Only eagerly import the return type if it's not too expensive (the current
23782365
// heuristic for that is if it's not a record type).
2379-
ImportedType importedType;
23802366
ImportDiagnosticAdder addDiag(*this, clangDecl,
23812367
clangDecl->getSourceRange().getBegin());
2382-
clang::QualType returnType = clangDecl->getReturnType();
2383-
if (auto elaborated = dyn_cast<clang::ElaboratedType>(returnType))
2384-
returnType = elaborated->desugar();
2385-
2386-
if (auto typedefType = dyn_cast<clang::TypedefType>(returnType)) {
2387-
if (isUnavailableInSwift(typedefType->getDecl())) {
2388-
if (auto clangEnum = findAnonymousEnumForTypedef(SwiftContext, typedefType)) {
2389-
// If this fails, it means that we need a stronger predicate for
2390-
// determining the relationship between an enum and typedef.
2391-
assert(clangEnum.value()->getIntegerType()->getCanonicalTypeInternal() ==
2392-
typedefType->getCanonicalTypeInternal());
2393-
if (auto swiftEnum = importDecl(*clangEnum, CurrentVersion)) {
2394-
importedType = {cast<TypeDecl>(swiftEnum)->getDeclaredInterfaceType(),
2395-
false};
2396-
}
2397-
}
2398-
}
2399-
}
2368+
clang::QualType returnType = desugarIfElaborated(clangDecl->getReturnType());
2369+
2370+
ImportedType importedType = importer::findOptionSetEnum(returnType, *this);
24002371

24012372
if (auto templateType =
24022373
dyn_cast<clang::TemplateTypeParmType>(returnType)) {
@@ -2460,9 +2431,7 @@ ClangImporter::Implementation::importParameterType(
24602431
std::optional<unsigned> completionHandlerErrorParamIndex,
24612432
ArrayRef<GenericTypeParamDecl *> genericParams,
24622433
llvm::function_ref<void(Diagnostic &&)> addImportDiagnosticFn) {
2463-
auto paramTy = param->getType();
2464-
if (auto elaborated = dyn_cast<clang::ElaboratedType>(paramTy))
2465-
paramTy = elaborated->desugar();
2434+
auto paramTy = desugarIfElaborated(param->getType());
24662435

24672436
ImportTypeKind importKind = paramIsCompletionHandler
24682437
? ImportTypeKind::CompletionHandlerParameter
@@ -2475,23 +2444,8 @@ ClangImporter::Implementation::importParameterType(
24752444
bool isConsuming = false;
24762445
bool isParamTypeImplicitlyUnwrapped = false;
24772446

2478-
// Sometimes we import unavailable typedefs as enums. If that's the case,
2479-
// use the enum, not the typedef here.
2480-
if (auto typedefType = dyn_cast<clang::TypedefType>(paramTy.getTypePtr())) {
2481-
if (isUnavailableInSwift(typedefType->getDecl())) {
2482-
if (auto clangEnum =
2483-
findAnonymousEnumForTypedef(SwiftContext, typedefType)) {
2484-
// If this fails, it means that we need a stronger predicate for
2485-
// determining the relationship between an enum and typedef.
2486-
assert(clangEnum.value()
2487-
->getIntegerType()
2488-
->getCanonicalTypeInternal() ==
2489-
typedefType->getCanonicalTypeInternal());
2490-
if (auto swiftEnum = importDecl(*clangEnum, CurrentVersion)) {
2491-
swiftParamTy = cast<TypeDecl>(swiftEnum)->getDeclaredInterfaceType();
2492-
}
2493-
}
2494-
}
2447+
if (auto optionSetEnum = importer::findOptionSetEnum(paramTy, *this)) {
2448+
swiftParamTy = optionSetEnum.getType();
24952449
} else if (isa<clang::PointerType>(paramTy) &&
24962450
isa<clang::TemplateTypeParmType>(paramTy->getPointeeType())) {
24972451
auto pointeeType = paramTy->getPointeeType();
@@ -3211,26 +3165,8 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType(
32113165

32123166
ImportDiagnosticAdder addImportDiag(*this, clangDecl,
32133167
clangDecl->getLocation());
3214-
clang::QualType resultType = clangDecl->getReturnType();
3215-
if (auto elaborated = dyn_cast<clang::ElaboratedType>(resultType))
3216-
resultType = elaborated->desugar();
3217-
3218-
ImportedType importedType;
3219-
if (auto typedefType = dyn_cast<clang::TypedefType>(resultType.getTypePtr())) {
3220-
if (isUnavailableInSwift(typedefType->getDecl())) {
3221-
if (auto clangEnum = findAnonymousEnumForTypedef(SwiftContext, typedefType)) {
3222-
// If this fails, it means that we need a stronger predicate for
3223-
// determining the relationship between an enum and typedef.
3224-
assert(clangEnum.value()->getIntegerType()->getCanonicalTypeInternal() ==
3225-
typedefType->getCanonicalTypeInternal());
3226-
if (auto swiftEnum = importDecl(*clangEnum, CurrentVersion)) {
3227-
importedType = {cast<TypeDecl>(swiftEnum)->getDeclaredInterfaceType(),
3228-
false};
3229-
}
3230-
}
3231-
}
3232-
}
3233-
3168+
clang::QualType resultType = desugarIfElaborated(clangDecl->getReturnType());
3169+
ImportedType importedType = importer::findOptionSetEnum(resultType, *this);
32343170
if (!importedType)
32353171
importedType = importType(resultType, resultKind, addImportDiag,
32363172
allowNSUIntegerAsIntInResult, Bridgeability::Full,
@@ -3478,9 +3414,6 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType(
34783414
importedType.isImplicitlyUnwrapped()};
34793415
}
34803416

3481-
ImportedType findOptionSetType(clang::QualType type,
3482-
ClangImporter::Implementation &Impl);
3483-
34843417
ImportedType ClangImporter::Implementation::importAccessorParamsAndReturnType(
34853418
const DeclContext *dc, const clang::ObjCPropertyDecl *property,
34863419
const clang::ObjCMethodDecl *clangDecl, bool isFromSystemModule,
@@ -3506,11 +3439,11 @@ ImportedType ClangImporter::Implementation::importAccessorParamsAndReturnType(
35063439
if (!origDC)
35073440
return {Type(), false};
35083441

3509-
auto fieldType = isGetter ? clangDecl->getReturnType()
3510-
: clangDecl->getParamDecl(0)->getType();
3511-
3442+
auto fieldType =
3443+
desugarIfElaborated(isGetter ? clangDecl->getReturnType()
3444+
: clangDecl->getParamDecl(0)->getType());
35123445
// Import the property type, independent of what kind of accessor this is.
3513-
ImportedType importedType = findOptionSetType(fieldType, *this);
3446+
ImportedType importedType = importer::findOptionSetEnum(fieldType, *this);
35143447
if (!importedType)
35153448
importedType = importPropertyType(property, isFromSystemModule);
35163449
if (!importedType)

lib/ClangImporter/ImporterImpl.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2083,6 +2083,27 @@ bool hasEscapableAttr(const clang::RecordDecl *decl);
20832083

20842084
bool isViewType(const clang::CXXRecordDecl *decl);
20852085

2086+
inline const clang::Type *desugarIfElaborated(const clang::Type *type) {
2087+
if (auto elaborated = dyn_cast<clang::ElaboratedType>(type))
2088+
return elaborated->desugar().getTypePtr();
2089+
return type;
2090+
}
2091+
2092+
inline clang::QualType desugarIfElaborated(clang::QualType type) {
2093+
if (auto elaborated = dyn_cast<clang::ElaboratedType>(type))
2094+
return elaborated->desugar();
2095+
return type;
2096+
}
2097+
2098+
/// Option set enums are sometimes imported as typedefs which assign a name to
2099+
/// the type, but are unavailable in Swift.
2100+
///
2101+
/// If given such a typedef, this helper function retrieves and imports the
2102+
/// underlying enum type. Returns an empty ImportedType otherwise.
2103+
///
2104+
/// If \a type is an elaborated type, it should be desugared first.
2105+
ImportedType findOptionSetEnum(clang::QualType type,
2106+
ClangImporter::Implementation &Impl);
20862107
} // end namespace importer
20872108
} // end namespace swift
20882109

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// RUN: split-file %s %t
2+
// RUN: %target-swift-frontend -typecheck -verify %t/main.swift -I %t/include -module-name main
3+
// RUN: %target-swift-frontend -typecheck -verify %t/main.swift -I %t/include -module-name main -cxx-interoperability-mode=default
4+
// REQUIRES: objc_interop
5+
6+
//--- include/module.modulemap
7+
module ObjCModule {
8+
header "header.h"
9+
}
10+
11+
//--- include/header.h
12+
#include <Foundation/Foundation.h>
13+
14+
#define UNAVAILABLE API_UNAVAILABLE(macos)
15+
16+
UNAVAILABLE
17+
typedef NS_ENUM(NSUInteger, MyUnavailableEnum) {
18+
MyUnavailableEnumCase1 = 0,
19+
MyUnavailableEnumCase2,
20+
};
21+
22+
UNAVAILABLE
23+
void unavailableParam(MyUnavailableEnum e);
24+
25+
UNAVAILABLE
26+
MyUnavailableEnum unavailableReturn(void);
27+
28+
struct UNAVAILABLE UnavailableStruct {
29+
MyUnavailableEnum field;
30+
};
31+
32+
UNAVAILABLE
33+
@interface UnavailableClass
34+
@property (nonatomic, readonly) MyUnavailableEnum prop;
35+
@end
36+
37+
UNAVAILABLE
38+
__attribute__((swift_name("getter:UnavailableStruct.iProp(self:)")))
39+
inline MyUnavailableEnum getUnavailableInjProp(struct UnavailableStruct s) {
40+
return MyUnavailableEnumCase1;
41+
}
42+
43+
//--- main.swift
44+
import Foundation
45+
import ObjCModule
46+
47+
@available(*, unavailable)
48+
func useParam(_ e: MyUnavailableEnum) { unavailableParam(e) }
49+
50+
@available(*, unavailable)
51+
func useReturn() -> MyUnavailableEnum { return unavailableReturn() }
52+
53+
@available(*, unavailable)
54+
func useField(_ o: UnavailableStruct) -> MyUnavailableEnum { return o.field }
55+
56+
@available(*, unavailable)
57+
func useIProp(_ o: UnavailableStruct) -> MyUnavailableEnum { return o.iProp }
58+
59+
@available(*, unavailable)
60+
func useProp(_ o: UnavailableClass) -> MyUnavailableEnum { return o.prop }

0 commit comments

Comments
 (0)