Skip to content

Commit 274c4d9

Browse files
authored
Merge pull request #80557 from beccadax/the-case-of-the-missing-member
[ClangImporter] Fix import of aliased enum cases
2 parents fe98abb + 86704e3 commit 274c4d9

File tree

2 files changed

+48
-8
lines changed

2 files changed

+48
-8
lines changed

Diff for: lib/ClangImporter/ImportDecl.cpp

+45-5
Original file line numberDiff line numberDiff line change
@@ -1866,10 +1866,28 @@ namespace {
18661866
break;
18671867
}
18681868

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

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

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

1964-
if (unimported != constant && enumeratorDecl) {
1989+
// If `constant` wasn't the `canonConstant`, import it as an alias.
1990+
if (canonConstant != constant && enumeratorDecl) {
19651991
ImportedName importedName =
19661992
Impl.importFullName(constant, getActiveSwiftVersion());
19671993
Identifier name = importedName.getBaseIdentifier(Impl.SwiftContext);
@@ -1977,6 +2003,7 @@ namespace {
19772003
}
19782004
}
19792005

2006+
// Now import each of the constant's alternate names.
19802007
Impl.forEachDistinctName(constant,
19812008
[&](ImportedName newName,
19822009
ImportNameVersion nameVersion) -> bool {
@@ -2027,6 +2054,19 @@ namespace {
20272054
}
20282055
}
20292056

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

Diff for: test/ClangImporter/enum.swift

+3-3
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)