|
| 1 | +// RUN: rm -rf %t |
| 2 | +// RUN: mkdir -p %t |
| 3 | +// RUN: split-file --leading-lines %s %t |
| 4 | +// |
| 5 | +// Prepare the BMIs. |
| 6 | +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-module-interface -o %t/mod_a-part1.pcm %t/mod_a-part1.cppm |
| 7 | +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-module-interface -o %t/mod_a-part2.pcm %t/mod_a-part2.cppm |
| 8 | +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-module-interface -o %t/mod_a.pcm %t/mod_a.cppm -fmodule-file=mod_a:part2=%t/mod_a-part2.pcm -fmodule-file=mod_a:part1=%t/mod_a-part1.pcm |
| 9 | +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-module-interface -o %t/mod_b.pcm %t/mod_b.cppm -fmodule-file=mod_a:part2=%t/mod_a-part2.pcm -fmodule-file=mod_a=%t/mod_a.pcm -fmodule-file=mod_a:part1=%t/mod_a-part1.pcm |
| 10 | + |
| 11 | +// Below are two examples to trigger the construction of the parent map (which is necessary to trigger the bug this regression test is for). |
| 12 | +// Using ArrayBoundV2 checker: |
| 13 | +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -analyze -analyzer-checker=security,alpha.security -analyzer-output=text %t/test-array-bound-v2.cpp -fmodule-file=mod_a:part2=%t/mod_a-part2.pcm -fmodule-file=mod_a=%t/mod_a.pcm -fmodule-file=mod_a:part1=%t/mod_a-part1.pcm -fmodule-file=mod_b=%t/mod_b.pcm |
| 14 | +// Using a sanitized build: |
| 15 | +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fsanitize=unsigned-integer-overflow -fsanitize-undefined-ignore-overflow-pattern=all -emit-llvm -o %t/ignored %t/test-sanitized-build.cpp -fmodule-file=mod_a:part2=%t/mod_a-part2.pcm -fmodule-file=mod_a=%t/mod_a.pcm -fmodule-file=mod_a:part1=%t/mod_a-part1.pcm -fmodule-file=mod_b=%t/mod_b.pcm |
| 16 | + |
| 17 | +//--- mod_a-part1.cppm |
| 18 | +module; |
| 19 | +namespace mod_a { |
| 20 | +template <int> struct Important; |
| 21 | +} |
| 22 | + |
| 23 | +namespace mod_a { |
| 24 | +Important<0>& instantiate1(); |
| 25 | +} // namespace mod_a |
| 26 | +export module mod_a:part1; |
| 27 | + |
| 28 | +export namespace mod_a { |
| 29 | +using ::mod_a::instantiate1; |
| 30 | +} |
| 31 | + |
| 32 | +//--- mod_a-part2.cppm |
| 33 | +module; |
| 34 | +namespace mod_a { |
| 35 | +template <int> struct Important; |
| 36 | +} |
| 37 | + |
| 38 | +namespace mod_a { |
| 39 | +template <int N> Important<N>& instantiate2(); |
| 40 | +namespace part2InternalInstantiations { |
| 41 | +// During the construction of the parent map, we iterate over ClassTemplateDecl::specializations() for 'Important'. |
| 42 | +// After GH119333, the following instantiations get loaded between the call to spec_begin() and spec_end(). |
| 43 | +// This used to invalidate the begin iterator returned by spec_begin() by the time the end iterator is returned. |
| 44 | +// This is a regression test for that. |
| 45 | +Important<1> fn1(); |
| 46 | +Important<2> fn2(); |
| 47 | +Important<3> fn3(); |
| 48 | +Important<4> fn4(); |
| 49 | +Important<5> fn5(); |
| 50 | +Important<6> fn6(); |
| 51 | +Important<7> fn7(); |
| 52 | +Important<8> fn8(); |
| 53 | +Important<9> fn9(); |
| 54 | +Important<10> fn10(); |
| 55 | +Important<11> fn11(); |
| 56 | +} |
| 57 | +} // namespace mod_a |
| 58 | +export module mod_a:part2; |
| 59 | + |
| 60 | +export namespace mod_a { |
| 61 | +using ::mod_a::instantiate2; |
| 62 | +} |
| 63 | + |
| 64 | +//--- mod_a.cppm |
| 65 | +export module mod_a; |
| 66 | +export import :part1; |
| 67 | +export import :part2; |
| 68 | + |
| 69 | +//--- mod_b.cppm |
| 70 | +export module mod_b; |
| 71 | +import mod_a; |
| 72 | + |
| 73 | +void a() { |
| 74 | + mod_a::instantiate1(); |
| 75 | + mod_a::instantiate2<42>(); |
| 76 | +} |
| 77 | + |
| 78 | +//--- test-array-bound-v2.cpp |
| 79 | +import mod_b; |
| 80 | + |
| 81 | +extern void someFunc(char* first, char* last); |
| 82 | +void triggerParentMapContextCreationThroughArrayBoundV2() { |
| 83 | + // This code currently causes the ArrayBoundV2 checker to create the ParentMapContext. |
| 84 | + // Once it detects an access to buf[100], the checker looks through the parents to find '&' operator. |
| 85 | + // (this is needed since taking the address of past-the-end pointer is allowed by the checker) |
| 86 | + char buf[100]; |
| 87 | + someFunc(&buf[0], &buf[100]); |
| 88 | +} |
| 89 | + |
| 90 | +//--- test-sanitized-build.cpp |
| 91 | +import mod_b; |
| 92 | + |
| 93 | +extern void some(); |
| 94 | +void triggerParentMapContextCreationThroughSanitizedBuild(unsigned i) { |
| 95 | + // This code currently causes UBSan to create the ParentMapContext. |
| 96 | + // UBSan currently excludes the pattern below to avoid noise, and it relies on ParentMapContext to detect it. |
| 97 | + while (i--) |
| 98 | + some(); |
| 99 | +} |
0 commit comments