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, Visibility<[ClangOption, CC1Option]>, HelpText<"Set the maximum number of steps in constexpr function evaluation (0 = no limit)">, MarshallingInfoInt, "1048576">; +def fexpansion_limit_EQ : Joined<["-"], "fexpansion-limit=">, Group, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Set the maximum number of times a single expansion statement may be expanded (0 = no limit)">, + MarshallingInfoInt, "256">; def fexperimental_new_constant_interpreter : Flag<["-"], "fexperimental-new-constant-interpreter">, 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 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 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; + +template +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 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 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) {} +}