Skip to content

Commit 0a26b44

Browse files
authored
Merge pull request #80780 from beccadax/the-case-of-the-missing-member-6.2
🌸 [ClangImporter] Fix import of aliased enum cases
2 parents 361ebea + 429128c commit 0a26b44

File tree

2 files changed

+48
-8
lines changed

2 files changed

+48
-8
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1865,10 +1865,28 @@ namespace {
18651865
break;
18661866
}
18671867

1868+
/// A table mapping each raw value used in this enum to the clang or
1869+
/// Swift decl for the "canonical" constant corresponding to that raw
1870+
/// value. The clang decls represent cases that haven't yet been imported;
1871+
/// the Swift decls represent cases that have been imported before.
1872+
///
1873+
/// The problem we are trying to solve here is that C allows several
1874+
/// constants in the same enum to have the same raw value, but Swift does
1875+
/// not. We must therefore resolve collisions by selecting one case to be
1876+
/// the "canonical" one that will be imported as an \c EnumElementDecl
1877+
/// and importing the others as static \c VarDecl aliases of it. This
1878+
/// map knows which constants are canonical and can map a constant's raw
1879+
/// value to its corresponding canonical constant.
1880+
///
1881+
/// Note that unavailable constants don't get inserted into this table,
1882+
/// so if an unavailable constant has no available alias, it simply won't
1883+
/// be present here. (Potential raw value conflicts doesn't really matter
1884+
/// for them because they will be imported as unavailable anyway.)
18681885
llvm::SmallDenseMap<llvm::APSInt,
18691886
PointerUnion<const clang::EnumConstantDecl *,
18701887
EnumElementDecl *>, 8> canonicalEnumConstants;
18711888

1889+
// Fill in `canonicalEnumConstants` if it will be used.
18721890
if (enumKind == EnumKind::NonFrozenEnum ||
18731891
enumKind == EnumKind::FrozenEnum) {
18741892
for (auto constant : decl->enumerators()) {
@@ -1943,24 +1961,32 @@ namespace {
19431961
SwiftDeclConverter(Impl, getActiveSwiftVersion())
19441962
.importEnumCase(constant, decl, cast<EnumDecl>(result));
19451963
} else {
1946-
const clang::EnumConstantDecl *unimported =
1964+
// Will initially be nullptr if `canonicalCaseIter` points to a
1965+
// memoized result.
1966+
const clang::EnumConstantDecl *canonConstant =
19471967
canonicalCaseIter->
19481968
second.dyn_cast<const clang::EnumConstantDecl *>();
19491969

1950-
// Import the canonical enumerator for this case first.
1951-
if (unimported) {
1970+
// First, either import the canonical constant for this case,
1971+
// or extract the memoized result of a previous import (and use it
1972+
// to populate `canonConstant`).
1973+
if (canonConstant) {
19521974
enumeratorDecl = SwiftDeclConverter(Impl, getActiveSwiftVersion())
1953-
.importEnumCase(unimported, decl, cast<EnumDecl>(result));
1975+
.importEnumCase(canonConstant, decl, cast<EnumDecl>(result));
19541976
if (enumeratorDecl) {
1977+
// Memoize so we end up in the `else` branch next time.
19551978
canonicalCaseIter->getSecond() =
19561979
cast<EnumElementDecl>(enumeratorDecl);
19571980
}
19581981
} else {
19591982
enumeratorDecl =
19601983
canonicalCaseIter->second.get<EnumElementDecl *>();
1984+
canonConstant =
1985+
cast<clang::EnumConstantDecl>(enumeratorDecl->getClangDecl());
19611986
}
19621987

1963-
if (unimported != constant && enumeratorDecl) {
1988+
// If `constant` wasn't the `canonConstant`, import it as an alias.
1989+
if (canonConstant != constant && enumeratorDecl) {
19641990
ImportedName importedName =
19651991
Impl.importFullName(constant, getActiveSwiftVersion());
19661992
Identifier name = importedName.getBaseIdentifier(Impl.SwiftContext);
@@ -1976,6 +2002,7 @@ namespace {
19762002
}
19772003
}
19782004

2005+
// Now import each of the constant's alternate names.
19792006
Impl.forEachDistinctName(constant,
19802007
[&](ImportedName newName,
19812008
ImportNameVersion nameVersion) -> bool {
@@ -2026,6 +2053,19 @@ namespace {
20262053
}
20272054
}
20282055

2056+
// We don't always add an imported canonical constant to the enum's
2057+
// members right away, but we should have by the time we leave the loop.
2058+
// Verify that they are all in the enum's member list. (rdar://148213237)
2059+
if (CONDITIONAL_ASSERT_enabled()) {
2060+
for (const auto &entry : canonicalEnumConstants) {
2061+
auto importedCase = entry.second.dyn_cast<EnumElementDecl *>();
2062+
if (!importedCase)
2063+
continue;
2064+
2065+
ASSERT(llvm::is_contained(result->getCurrentMembers(), importedCase));
2066+
}
2067+
}
2068+
20292069
return result;
20302070
}
20312071

test/ClangImporter/enum.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck %s -verify
2-
// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck %s 2>&1 | %FileCheck %s
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck %s -verify -compiler-assertions
2+
// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck %s -compiler-assertions 2>&1 | %FileCheck %s
33
// -- Check that we can successfully round-trip.
4-
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -D IRGEN -emit-ir -primary-file %s | %FileCheck -check-prefix=CHECK-IR %s
4+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -D IRGEN -emit-ir -primary-file %s -compiler-assertions | %FileCheck -check-prefix=CHECK-IR %s
55

66
// REQUIRES: objc_interop
77

0 commit comments

Comments
 (0)