-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[Clang] [C++26] Expansion Statements (Part 10: Expansion Limit) #169689
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Sirraide
wants to merge
1
commit into
users/Sirraide/expansion-stmts-9-control-flow
Choose a base branch
from
users/Sirraide/expansion-stmts-10-limits
base: users/Sirraide/expansion-stmts-9-control-flow
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
[Clang] [C++26] Expansion Statements (Part 10: Expansion Limit) #169689
Sirraide
wants to merge
1
commit into
users/Sirraide/expansion-stmts-9-control-flow
from
users/Sirraide/expansion-stmts-10-limits
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This was referenced Nov 26, 2025
Member
Author
This was referenced Nov 26, 2025
Member
|
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clang-codegen Author: None (Sirraide) ChangesFull diff: https://github.com/llvm/llvm-project/pull/169689.diff 7 Files Affected:
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 5115c175849e1..222d587b48a0b 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -169,6 +169,10 @@ def err_expansion_size_expr_not_ice : Error<
"expansion size is not a constant expression">;
def err_expansion_size_negative : Error<
"expansion size must not be negative (was %0)">;
+def err_expansion_too_big : Error<
+ "expansion size %0 exceeds maximum configured size %1">;
+def note_use_fexpansion_limit : Note<
+ "use -fexpansion-limit=N to adjust this limit">;
// Semantic analysis of constant literals.
def ext_predef_outside_function : Warning<
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index 40fc66ea12e34..315cb4dc5e1cf 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -377,6 +377,7 @@ LANGOPT(ConstexprCallDepth, 32, 512, Benign,
"maximum constexpr call depth")
LANGOPT(ConstexprStepLimit, 32, 1048576, Benign,
"maximum constexpr evaluation steps")
+LANGOPT(MaxTemplateForExpansions, 32, 256, Benign, "maximum template for expansions")
LANGOPT(EnableNewConstInterp, 1, 0, Benign,
"enable the experimental new constant interpreter")
LANGOPT(BracketDepth, 32, 256, Benign,
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 0e88656c5e1bc..86867b4814eeb 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -2054,6 +2054,10 @@ def fconstexpr_steps_EQ : Joined<["-"], "fconstexpr-steps=">, Group<f_Group>,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Set the maximum number of steps in constexpr function evaluation (0 = no limit)">,
MarshallingInfoInt<LangOpts<"ConstexprStepLimit">, "1048576">;
+def fexpansion_limit_EQ : Joined<["-"], "fexpansion-limit=">, Group<f_Group>,
+ Visibility<[ClangOption, CC1Option]>,
+ HelpText<"Set the maximum number of times a single expansion statement may be expanded (0 = no limit)">,
+ MarshallingInfoInt<LangOpts<"MaxTemplateForExpansions">, "256">;
def fexperimental_new_constant_interpreter : Flag<["-"], "fexperimental-new-constant-interpreter">, Group<f_Group>,
HelpText<"Enable the experimental new constant interpreter">,
Visibility<[ClangOption, CC1Option]>,
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index c5d40c9825fab..649d4c3e4ca97 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -6358,6 +6358,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
Args.AddLastArg(CmdArgs, options::OPT_foperator_arrow_depth_EQ);
Args.AddLastArg(CmdArgs, options::OPT_fconstexpr_depth_EQ);
Args.AddLastArg(CmdArgs, options::OPT_fconstexpr_steps_EQ);
+ Args.AddLastArg(CmdArgs, options::OPT_fexpansion_limit_EQ);
Args.AddLastArg(CmdArgs, options::OPT_fexperimental_library);
diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp
index fcc951503deb9..ad481df7e7d6a 100644
--- a/clang/lib/Sema/SemaExpand.cpp
+++ b/clang/lib/Sema/SemaExpand.cpp
@@ -43,6 +43,18 @@ struct IterableExpansionStmtData {
};
} // namespace
+static bool CheckExpansionSize(Sema &S, uint64_t NumInstantiations,
+ SourceLocation Loc) {
+ unsigned Max = S.LangOpts.MaxTemplateForExpansions;
+ if (Max != 0 && NumInstantiations > Max) {
+ S.Diag(Loc, diag::err_expansion_too_big) << NumInstantiations << Max;
+ S.Diag(Loc, diag::note_use_fexpansion_limit);
+ return true;
+ }
+
+ return false;
+}
+
// Build a 'DeclRefExpr' designating the template parameter '__N'.
static DeclRefExpr *BuildIndexDRE(Sema &S, CXXExpansionStmtDecl *ESD) {
return S.BuildDeclRefExpr(ESD->getIndexTemplateParm(),
@@ -198,6 +210,9 @@ static StmtResult BuildDestructuringCXXExpansionStmt(
return StmtError();
}
+ if (CheckExpansionSize(S, *Arity, ColonLoc))
+ return StmtError();
+
QualType AutoRRef = S.Context.getAutoRRefDeductType();
SmallVector<BindingDecl *> Bindings;
for (unsigned I = 0; I < *Arity; ++I)
@@ -399,6 +414,9 @@ StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) {
if (!NumInstantiations)
return StmtError();
+ if (CheckExpansionSize(*this, *NumInstantiations, Expansion->getColonLoc()))
+ return StmtError();
+
// Collect shared statements.
SmallVector<Stmt *, 1> Shared;
if (Expansion->getInit())
diff --git a/clang/test/SemaCXX/cxx2c-expansion-stmts-limit.cpp b/clang/test/SemaCXX/cxx2c-expansion-stmts-limit.cpp
new file mode 100644
index 0000000000000..8411d6987565c
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2c-expansion-stmts-limit.cpp
@@ -0,0 +1,68 @@
+// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -fexpansion-limit=32 -verify
+
+void g(int);
+
+template <__SIZE_TYPE__ size>
+struct String {
+ char data[size];
+
+ template <__SIZE_TYPE__ n>
+ constexpr String(const char (&str)[n]) { __builtin_memcpy(data, str, n); }
+
+ constexpr const char* begin() const { return data; }
+ constexpr const char* end() const { return data + size - 1; }
+};
+
+template <__SIZE_TYPE__ n>
+String(const char (&str)[n]) -> String<n>;
+
+template <typename T, __SIZE_TYPE__ size>
+struct Array {
+ T data[size]{};
+ constexpr const T* begin() const { return data; }
+ constexpr const T* end() const { return data + size; }
+};
+
+void expansion_size() {
+ static constexpr Array<int, 32> almost_too_big;
+ template for (auto x : almost_too_big) g(x);
+ template for (constexpr auto x : almost_too_big) g(x);
+
+ static constexpr Array<int, 33> too_big;
+ template for (auto x : too_big) g(x); // expected-error {{expansion size 33 exceeds maximum configured size 32}} expected-note {{use -fexpansion-limit=N to adjust this limit}}
+ template for (constexpr auto x : too_big) g(x); // expected-error {{expansion size 33 exceeds maximum configured size 32}} expected-note {{use -fexpansion-limit=N to adjust this limit}}
+
+ static constexpr String big{"1234567890123456789012345678901234567890234567890"};
+ template for (auto x : big) g(x); // expected-error {{expansion size 49 exceeds maximum configured size 32}} expected-note {{use -fexpansion-limit=N to adjust this limit}}
+ template for (constexpr auto x : big) g(x); // expected-error {{expansion size 49 exceeds maximum configured size 32}} expected-note {{use -fexpansion-limit=N to adjust this limit}}
+
+ template for (auto x : {1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+ 31, 32}) g(x);
+ template for (constexpr auto x : {1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+ 31, 32}) g(x);
+
+ template for (auto x : {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // expected-error {{expansion size 33 exceeds maximum configured size 32}} expected-note {{use -fexpansion-limit=N to adjust this limit}}
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+ 31, 32, 33}) g(x);
+ template for (constexpr auto x : {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // expected-error {{expansion size 33 exceeds maximum configured size 32}} expected-note {{use -fexpansion-limit=N to adjust this limit}}
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+ 31, 32, 33}) g(x);
+
+ int huge[1'000'000'000];
+ template for (auto x : huge) {} // expected-error {{expansion size 1000000000 exceeds maximum configured size 32}} expected-note {{use -fexpansion-limit=N to adjust this limit}}
+}
+
+void array_too_big() {
+ int ok[32];
+ int too_big[33];
+
+ template for (auto x : ok) {}
+ template for (auto x : too_big) {} // expected-error {{expansion size 33 exceeds maximum configured size 32}} \
+ expected-note {{use -fexpansion-limit=N to adjust this limit}}
+}
diff --git a/clang/test/SemaCXX/cxx2c-fexpansion-statements.cpp b/clang/test/SemaCXX/cxx2c-fexpansion-statements.cpp
new file mode 100644
index 0000000000000..2c80c392e400d
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2c-fexpansion-statements.cpp
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -fexpansion-limit=0 -verify
+// expected-no-diagnostics
+
+// Test that passing =0 disables the limit.
+
+void big() {
+ int ok[500];
+ template for (auto x : ok) {}
+}
|
7314e79 to
9a24859
Compare
00062fb to
162cc15
Compare
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
c++26
clang:codegen
IR generation bugs: mangling, exceptions, etc.
clang:driver
'clang' and 'clang++' user-facing binaries. Not 'clang-cl'
clang:frontend
Language frontend issues, e.g. anything involving "Sema"
clang
Clang issues not falling into any other category
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.

This implements a limit on the maximum number of expansions; this isn’t in the standard (there is nothing on this in [implimits]), but my reasoning is that if someone makes a mistake and inadvertently causes e.g. 1 000 000 000 instantiations, Clang is going to hang... basically for ever (for comparison, 1 million instantiations takes about 3 seconds provided the body of the expansion statement is empty).
For now, I’ve set the limit to
256, but that’s probably too low. I don’t have strong opinions on the exact value; I definitely do think that there should be a limit though. We could in theory reuse the constexpr-steps value, but it feels a bit weird because this isn’t really doing constant evaluation so much as just template instantiation.