Skip to content

Commit ba1e84f

Browse files
committed
[C++20] [Modules] Handle inconsistent deduced function return type from importing modules
Close #78830 Close #60085 The direct reason of the issues is that in a module unit, the return type of a function is deduced to a concrete type (e.g., int) but in the other module unit, the return type of the same function is not deduced yet (e.g, auto). Then when we importing the 2 modules, the later function is selected but the code generator doesn't know how to generate the auto type. So here is the crash. The tricky part here is that, when the ASTReader reads the second unreduced function, it finds the reading function has the same signature with the already read deduced one and they have the same ODRHash. So that the ASTReader skips loading the function body of the unreduced function then the code generator can't infer the undeduced type like it usually can. Also this is generally fine for functions without deducing type since it is sufficient to emit a function call without the function body. Also in fact, we've already handled the case that the functon has deduced type and its deducing state is inconsist in different modules: https://github.com/llvm/llvm-project/blob/3ea92ea2f9d236569f82825cdba6d59bcc22495c/clang/lib/Serialization/ASTReader.cpp#L9531-L9544 and https://github.com/llvm/llvm-project/blob/3ea92ea2f9d236569f82825cdba6d59bcc22495c/clang/lib/Serialization/ASTReaderDecl.cpp#L3643-L3647. We've handled the case: (1) If we read the undeduced functions first and read the deduced functions later, the compiler will propagate the deduced type info for redecls in the end of the reading. (2) If we read the deduced functions first and read the undeduced functions later, we will propagae the deduced type info when we **complete the redecl chain**. However, in the reporting issues, we're in the second case and reproducer didn't trigger the action to complete the redecl chain. So here is the crash. Then it is obvious how should fix the problem. We should complete the redecl chain for undeduced function types in the end of the reading for the second case.
1 parent 7da7695 commit ba1e84f

File tree

5 files changed

+172
-6
lines changed

5 files changed

+172
-6
lines changed

clang/docs/ReleaseNotes.rst

+5
Original file line numberDiff line numberDiff line change
@@ -1049,6 +1049,11 @@ Bug Fixes to C++ Support
10491049

10501050
- Set the ``__cpp_auto_cast`` feature test macro in C++23 mode.
10511051

1052+
- Fix crash for inconsistent deducing state of function return types
1053+
in importing modules.
1054+
Fixes (`#78830 <https://github.com/llvm/llvm-project/issues/78830>`_)
1055+
Fixes (`#60085 <https://github.com/llvm/llvm-project/issues/60085>`_)
1056+
10521057
Bug Fixes to AST Handling
10531058
^^^^^^^^^^^^^^^^^^^^^^^^^
10541059
- Fixed an import failure of recursive friend class template.

clang/include/clang/Serialization/ASTReader.h

+4
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,10 @@ class ASTReader
550550
/// declaration and the value is the deduced return type.
551551
llvm::SmallMapVector<FunctionDecl *, QualType, 4> PendingDeducedTypeUpdates;
552552

553+
/// Functions has undededuced return type and we wish we can find the deduced
554+
/// return type by iterating the redecls in other modules.
555+
llvm::SmallVector<FunctionDecl *, 4> PendingUndeducedFunctionDecls;
556+
553557
/// Declarations that have been imported and have typedef names for
554558
/// linkage purposes.
555559
llvm::DenseMap<std::pair<DeclContext *, IdentifierInfo *>, NamedDecl *>

clang/lib/Serialization/ASTReader.cpp

+22-6
Original file line numberDiff line numberDiff line change
@@ -9534,12 +9534,21 @@ void ASTReader::finishPendingActions() {
95349534
auto *FD = PendingDeducedFunctionTypes[I].first;
95359535
FD->setType(GetType(PendingDeducedFunctionTypes[I].second));
95369536

9537-
// If we gave a function a deduced return type, remember that we need to
9538-
// propagate that along the redeclaration chain.
9539-
auto *DT = FD->getReturnType()->getContainedDeducedType();
9540-
if (DT && DT->isDeduced())
9541-
PendingDeducedTypeUpdates.insert(
9542-
{FD->getCanonicalDecl(), FD->getReturnType()});
9537+
if (auto *DT = FD->getReturnType()->getContainedDeducedType()) {
9538+
// If we gave a function a deduced return type, remember that we need to
9539+
// propagate that along the redeclaration chain.
9540+
if (DT->isDeduced()) {
9541+
PendingDeducedTypeUpdates.insert(
9542+
{FD->getCanonicalDecl(), FD->getReturnType()});
9543+
continue;
9544+
}
9545+
9546+
// The function has undeduced DeduceType return type. We hope we can
9547+
// find the deduced type by iterating the redecls in other modules
9548+
// later.
9549+
PendingUndeducedFunctionDecls.push_back(FD);
9550+
continue;
9551+
}
95439552
}
95449553
PendingDeducedFunctionTypes.clear();
95459554

@@ -10105,6 +10114,13 @@ void ASTReader::FinishedDeserializing() {
1010510114
getContext().adjustDeducedFunctionResultType(Update.first,
1010610115
Update.second);
1010710116
}
10117+
10118+
auto UDTUpdates = std::move(PendingUndeducedFunctionDecls);
10119+
PendingUndeducedFunctionDecls.clear();
10120+
// We hope we can find the deduced type for the functions by iterating
10121+
// redeclarations in other modules.
10122+
for (FunctionDecl *UndeducedFD : UDTUpdates)
10123+
(void)UndeducedFD->getMostRecentDecl();
1010810124
}
1010910125

1011010126
if (ReadTimer)

clang/test/Modules/pr60085.cppm

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir %t
3+
// RUN: split-file %s %t
4+
//
5+
// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/d.cppm \
6+
// RUN: -emit-module-interface -o %t/d.pcm
7+
// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/c.cppm \
8+
// RUN: -emit-module-interface -o %t/c.pcm -fprebuilt-module-path=%t
9+
// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/b.cppm \
10+
// RUN: -emit-module-interface -o %t/b.pcm -fprebuilt-module-path=%t
11+
// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/a.cppm \
12+
// RUN: -emit-module-interface -o %t/a.pcm -fprebuilt-module-path=%t
13+
// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/a.pcm \
14+
// RUN: -S -emit-llvm -disable-llvm-passes -o - -fprebuilt-module-path=%t \
15+
// RUN: | FileCheck %t/a.cppm
16+
17+
//--- d.cppm
18+
export module d;
19+
20+
export template<typename>
21+
struct integer {
22+
using type = int;
23+
24+
static constexpr auto value() {
25+
return 0;
26+
}
27+
28+
friend constexpr void f(integer const x) {
29+
x.value();
30+
}
31+
};
32+
33+
export constexpr void ddd(auto const value) {
34+
f(value);
35+
}
36+
37+
38+
template<typename T>
39+
constexpr auto dd = T();
40+
41+
export template<typename T>
42+
constexpr auto d() {
43+
dd<T>;
44+
}
45+
46+
//--- c.cppm
47+
export module c;
48+
49+
import d;
50+
51+
template<typename T>
52+
auto cc = T();
53+
54+
auto c() {
55+
cc<integer<int>>;
56+
integer<int>().value();
57+
}
58+
59+
//--- b.cppm
60+
export module b;
61+
62+
import d;
63+
64+
auto b() {
65+
integer<int>::type;
66+
}
67+
68+
//--- a.cppm
69+
export module a;
70+
71+
import b;
72+
import c;
73+
import d;
74+
75+
constexpr void aa() {
76+
d<integer<unsigned>>();
77+
ddd(integer<int>());
78+
}
79+
80+
export extern "C" void a() {
81+
aa();
82+
}
83+
84+
// Checks that we emit the IR successfully.
85+
// CHECK: define{{.*}}@a(

clang/test/Modules/pr78830.cppm

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir -p %t
3+
// RUN: split-file %s %t
4+
//
5+
// RUN: %clang_cc1 -std=c++20 %t/Type.cppm -emit-module-interface -o \
6+
// RUN: %t/MyVec-Type.pcm -triple=x86_64-linux-gnu
7+
// RUN:%clang_cc1 -std=c++20 %t/Vec.cppm -emit-module-interface -o \
8+
// RUN: %t/MyVec-Vec.pcm -fmodule-file=MyVec:Type=%t/MyVec-Type.pcm \
9+
// RUN: -triple=x86_64-linux-gnu
10+
// RUN: %clang_cc1 -std=c++20 %t/Vec2.cppm -emit-module-interface -o \
11+
// RUN: %t/MyVec-Vec2.pcm -fmodule-file=MyVec:Type=%t/MyVec-Type.pcm \
12+
// RUN: -triple=x86_64-linux-gnu
13+
// RUN: %clang_cc1 -std=c++20 %t/Calculator.cppm -emit-module-interface -o \
14+
// RUN: %t/MyVec-Calculator.pcm -fmodule-file=MyVec:Vec=%t/MyVec-Vec.pcm \
15+
// RUN: -fmodule-file=MyVec:Vec2=%t/MyVec-Vec2.pcm \
16+
// RUN: -fmodule-file=MyVec:Type=%t/MyVec-Type.pcm \
17+
// RUN: -triple=x86_64-linux-gnu
18+
// RUN: %clang_cc1 -std=c++20 %t/MyVec-Calculator.pcm -S -emit-llvm \
19+
// RUN: -fmodule-file=MyVec:Vec=%t/MyVec-Vec.pcm \
20+
// RUN: -fmodule-file=MyVec:Vec2=%t/MyVec-Vec2.pcm \
21+
// RUN: -fmodule-file=MyVec:Type=%t/MyVec-Type.pcm \
22+
// RUN: -triple=x86_64-linux-gnu -o - \
23+
// RUN: | FileCheck %t/Calculator.cppm
24+
25+
//--- Type.cppm
26+
export module MyVec:Type;
27+
28+
template <class T> struct Size {
29+
auto total() const { return 1; }
30+
};
31+
32+
//--- Vec.cppm
33+
export module MyVec:Vec;
34+
import :Type;
35+
36+
int size_ = Size<int>().total();
37+
38+
//--- Vec2.cppm
39+
export module MyVec:Vec2;
40+
import :Type;
41+
42+
struct Vec2 {
43+
Size<int> size_;
44+
};
45+
46+
//--- Calculator.cppm
47+
export module MyVec:Calculator;
48+
49+
import :Vec;
50+
import :Vec2;
51+
52+
auto Calculate() { return Size<int>().total(); };
53+
54+
// Check the emitted module initializer to make sure we generate the module unit
55+
// successfully.
56+
// CHECK: @_ZW5MyVec9Calculatev

0 commit comments

Comments
 (0)