Skip to content

[Clang] Increase the default expression nesting limit #132021

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

Merged
merged 2 commits into from
Mar 19, 2025

Conversation

cor3ntin
Copy link
Contributor

This iterates on #104717 (which we had to revert)

In a bid to increase our chances of success, we try to avoid blowing up the stack by

  • Using runWithSufficientStackSpace in ParseCompoundStatement
  • Reducing the size of StmtVector a bit
  • Reducing the size of DeclsInGroup a bit
  • Removing a few ParsedAttributes from the stacks in places where they are not strictly necessary. ParsedAttributes is a huge object

On a 64 bits system, the following stack size reductions are observed

ParseStatementOrDeclarationAfterAttributes:  344 -> 264
ParseStatementOrDeclaration: 520 -> 376
ParseCompoundStatementBody: 1080 -> 1016
ParseDeclaration: 264 -> 120

Fixes #94728

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Mar 19, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 19, 2025

@llvm/pr-subscribers-clang

Author: cor3ntin (cor3ntin)

Changes

This iterates on #104717 (which we had to revert)

In a bid to increase our chances of success, we try to avoid blowing up the stack by

  • Using runWithSufficientStackSpace in ParseCompoundStatement
  • Reducing the size of StmtVector a bit
  • Reducing the size of DeclsInGroup a bit
  • Removing a few ParsedAttributes from the stacks in places where they are not strictly necessary. ParsedAttributes is a huge object

On a 64 bits system, the following stack size reductions are observed

ParseStatementOrDeclarationAfterAttributes:  344 -> 264
ParseStatementOrDeclaration: 520 -> 376
ParseCompoundStatementBody: 1080 -> 1016
ParseDeclaration: 264 -> 120

Fixes #94728


Full diff: https://github.com/llvm/llvm-project/pull/132021.diff

10 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+3-1)
  • (modified) clang/include/clang/Driver/Options.td (+1-1)
  • (modified) clang/include/clang/Parse/Parser.h (+5-1)
  • (modified) clang/include/clang/Sema/ParsedAttr.h (+13-4)
  • (modified) clang/lib/Parse/ParseDecl.cpp (+2-3)
  • (modified) clang/lib/Parse/ParseStmt.cpp (+12-14)
  • (modified) clang/lib/Parse/ParseTemplate.cpp (+7)
  • (modified) clang/lib/Parse/Parser.cpp (+5-3)
  • (modified) clang/lib/Sema/ParsedAttr.cpp (+8-13)
  • (modified) clang/test/Parser/parser_overflow.c (+2-2)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 0adbc19f40096..af72e80cc1a4b 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -169,6 +169,8 @@ Modified Compiler Flags
   the behavior of ``-mtp`` in gcc. This changes the default behavior for ARM targets that provide the ``TPIDRURO`` register as this will be used instead of a call to the ``__aeabi_read_tp``.
   Programs that use ``__aeabi_read_tp`` but do not use the ``TPIDRURO`` register must use ``-mtp=soft``. Fixes #123864
 
+- The compiler flag `-fbracket-depth` default value is increased from 256 to 2048. (#GH94728)
+
 Removed Compiler Flags
 -------------------------
 
@@ -264,7 +266,7 @@ Improvements to Clang's diagnostics
   as function arguments or return value respectively. Note that
   :doc:`ThreadSafetyAnalysis` still does not perform alias analysis. The
   feature will be default-enabled with ``-Wthread-safety`` in a future release.
-- The ``-Wsign-compare`` warning now treats expressions with bitwise not(~) and minus(-) as signed integers 
+- The ``-Wsign-compare`` warning now treats expressions with bitwise not(~) and minus(-) as signed integers
   except for the case where the operand is an unsigned integer
   and throws warning if they are compared with unsigned integers (##18878).
 - The ``-Wunnecessary-virtual-specifier`` warning has been added to warn about
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 66ae8f1c7f064..d41c7f20feba9 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -8161,7 +8161,7 @@ def fapply_global_visibility_to_externs : Flag<["-"], "fapply-global-visibility-
   MarshallingInfoFlag<LangOpts<"SetVisibilityForExternDecls">>;
 def fbracket_depth : Separate<["-"], "fbracket-depth">,
   HelpText<"Maximum nesting level for parentheses, brackets, and braces">,
-  MarshallingInfoInt<LangOpts<"BracketDepth">, "256">;
+  MarshallingInfoInt<LangOpts<"BracketDepth">, "2048">;
 defm const_strings : BoolOption<"f", "const-strings",
   LangOpts<"ConstStrings">, DefaultFalse,
   PosFlag<SetTrue, [], [ClangOption, CC1Option], "Use">,
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 1c8caf55a546d..fbe2865b1b7c1 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -91,6 +91,8 @@ class Parser : public CodeCompletionHandler {
 
   DiagnosticsEngine &Diags;
 
+  StackExhaustionHandler StackHandler;
+
   /// ScopeCache - Cache scopes to reduce malloc traffic.
   enum { ScopeCacheSize = 16 };
   unsigned NumCachedScopes;
@@ -518,7 +520,7 @@ class Parser : public CodeCompletionHandler {
   typedef Sema::FullExprArg FullExprArg;
 
   /// A SmallVector of statements.
-  typedef SmallVector<Stmt *, 32> StmtVector;
+  typedef SmallVector<Stmt *, 24> StmtVector;
 
   // Parsing methods.
 
@@ -3842,6 +3844,8 @@ class Parser : public CodeCompletionHandler {
   DeclGroupPtrTy ParseTemplateDeclarationOrSpecialization(
       DeclaratorContext Context, SourceLocation &DeclEnd,
       ParsedAttributes &AccessAttrs, AccessSpecifier AS);
+  clang::Parser::DeclGroupPtrTy ParseTemplateDeclarationOrSpecialization(
+      DeclaratorContext Context, SourceLocation &DeclEnd, AccessSpecifier AS);
   DeclGroupPtrTy ParseDeclarationAfterTemplate(
       DeclaratorContext Context, ParsedTemplateInfo &TemplateInfo,
       ParsingDeclRAIIObject &DiagsFromParams, SourceLocation &DeclEnd,
diff --git a/clang/include/clang/Sema/ParsedAttr.h b/clang/include/clang/Sema/ParsedAttr.h
index e1faab205f647..b88b871dc8821 100644
--- a/clang/include/clang/Sema/ParsedAttr.h
+++ b/clang/include/clang/Sema/ParsedAttr.h
@@ -970,6 +970,14 @@ class ParsedAttributes : public ParsedAttributesView {
     pool.takeAllFrom(Other.pool);
   }
 
+  void takeAllAtEndFrom(ParsedAttributes &Other) {
+    assert(&Other != this &&
+           "ParsedAttributes can't take attributes from itself");
+    addAllAtEnd(Other.begin(), Other.end());
+    Other.clearListOnly();
+    pool.takeAllFrom(Other.pool);
+  }
+
   void takeOneFrom(ParsedAttributes &Other, ParsedAttr *PA) {
     assert(&Other != this &&
            "ParsedAttributes can't take attribute from itself");
@@ -1067,10 +1075,11 @@ class ParsedAttributes : public ParsedAttributesView {
   mutable AttributePool pool;
 };
 
-/// Consumes the attributes from `First` and `Second` and concatenates them into
-/// `Result`. Sets `Result.Range` to the combined range of `First` and `Second`.
-void takeAndConcatenateAttrs(ParsedAttributes &First, ParsedAttributes &Second,
-                             ParsedAttributes &Result);
+/// Consumes the attributes from `Second` and concatenates them
+/// at the end of `First`. Sets `First.Range`
+/// to the combined range of `First` and `Second`.
+void takeAndConcatenateAttrs(ParsedAttributes &First,
+                             ParsedAttributes &&Second);
 
 /// These constants match the enumerated choices of
 /// err_attribute_argument_n_type and err_attribute_argument_type.
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 82b394d5b4ca6..9ca3e2b5756ca 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2076,10 +2076,9 @@ Parser::DeclGroupPtrTy Parser::ParseDeclaration(DeclaratorContext Context,
     ProhibitAttributes(DeclSpecAttrs);
     return ParseNamespace(Context, DeclEnd);
   case tok::kw_using: {
-    ParsedAttributes Attrs(AttrFactory);
-    takeAndConcatenateAttrs(DeclAttrs, DeclSpecAttrs, Attrs);
+    takeAndConcatenateAttrs(DeclAttrs, std::move(DeclSpecAttrs));
     return ParseUsingDirectiveOrDeclaration(Context, ParsedTemplateInfo(),
-                                            DeclEnd, Attrs);
+                                            DeclEnd, DeclAttrs);
   }
   case tok::kw_static_assert:
   case tok::kw__Static_assert:
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 623b4c799327c..150b2879fc94f 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -126,18 +126,15 @@ Parser::ParseStatementOrDeclaration(StmtVector &Stmts,
       Stmts, StmtCtx, TrailingElseLoc, CXX11Attrs, GNUOrMSAttrs);
   MaybeDestroyTemplateIds();
 
-  // Attributes that are left should all go on the statement, so concatenate the
-  // two lists.
-  ParsedAttributes Attrs(AttrFactory);
-  takeAndConcatenateAttrs(CXX11Attrs, GNUOrMSAttrs, Attrs);
+  takeAndConcatenateAttrs(CXX11Attrs, std::move(GNUOrMSAttrs));
 
-  assert((Attrs.empty() || Res.isInvalid() || Res.isUsable()) &&
+  assert((CXX11Attrs.empty() || Res.isInvalid() || Res.isUsable()) &&
          "attributes on empty statement");
 
-  if (Attrs.empty() || Res.isInvalid())
+  if (CXX11Attrs.empty() || Res.isInvalid())
     return Res;
 
-  return Actions.ActOnAttributedStmt(Attrs, Res.get());
+  return Actions.ActOnAttributedStmt(CXX11Attrs, Res.get());
 }
 
 namespace {
@@ -207,11 +204,10 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes(
       // Both C++11 and GNU attributes preceding the label appertain to the
       // label, so put them in a single list to pass on to
       // ParseLabeledStatement().
-      ParsedAttributes Attrs(AttrFactory);
-      takeAndConcatenateAttrs(CXX11Attrs, GNUAttrs, Attrs);
+      takeAndConcatenateAttrs(CXX11Attrs, std::move(GNUAttrs));
 
       // identifier ':' statement
-      return ParseLabeledStatement(Attrs, StmtCtx);
+      return ParseLabeledStatement(CXX11Attrs, StmtCtx);
     }
 
     // Look up the identifier, and typo-correct it to a keyword if it's not
@@ -302,9 +298,7 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes(
 
   case tok::kw_template: {
     SourceLocation DeclEnd;
-    ParsedAttributes Attrs(AttrFactory);
     ParseTemplateDeclarationOrSpecialization(DeclaratorContext::Block, DeclEnd,
-                                             Attrs,
                                              getAccessSpecifierIfPresent());
     return StmtError();
   }
@@ -1057,7 +1051,11 @@ StmtResult Parser::ParseCompoundStatement(bool isStmtExpr,
   ParseScope CompoundScope(this, ScopeFlags);
 
   // Parse the statements in the body.
-  return ParseCompoundStatementBody(isStmtExpr);
+  StmtResult R;
+  StackHandler.runWithSufficientStackSpace(Tok.getLocation(), [&, this]() {
+    R = ParseCompoundStatementBody(isStmtExpr);
+  });
+  return R;
 }
 
 /// Parse any pragmas at the start of the compound expression. We handle these
@@ -1222,7 +1220,7 @@ StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) {
   while (Tok.is(tok::kw___label__)) {
     SourceLocation LabelLoc = ConsumeToken();
 
-    SmallVector<Decl *, 8> DeclsInGroup;
+    SmallVector<Decl *, 4> DeclsInGroup;
     while (true) {
       if (Tok.isNot(tok::identifier)) {
         Diag(Tok, diag::err_expected) << tok::identifier;
diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp
index b2cb3d71e09f9..e8b4aee8c2686 100644
--- a/clang/lib/Parse/ParseTemplate.cpp
+++ b/clang/lib/Parse/ParseTemplate.cpp
@@ -179,6 +179,13 @@ Parser::DeclGroupPtrTy Parser::ParseTemplateDeclarationOrSpecialization(
       Context, TemplateInfo, ParsingTemplateParams, DeclEnd, AccessAttrs, AS);
 }
 
+Parser::DeclGroupPtrTy Parser::ParseTemplateDeclarationOrSpecialization(
+    DeclaratorContext Context, SourceLocation &DeclEnd, AccessSpecifier AS) {
+  ParsedAttributes AccessAttrs(AttrFactory);
+  return ParseTemplateDeclarationOrSpecialization(Context, DeclEnd, AccessAttrs,
+                                                  AS);
+}
+
 /// Parse a single declaration that declares a template,
 /// template specialization, or explicit instantiation of a template.
 ///
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 83dd7b17c73b8..6128bc58d69c0 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -17,6 +17,7 @@
 #include "clang/AST/DeclTemplate.h"
 #include "clang/Basic/DiagnosticParse.h"
 #include "clang/Basic/FileManager.h"
+#include "clang/Basic/StackExhaustionHandler.h"
 #include "clang/Parse/RAIIObjectsForParser.h"
 #include "clang/Sema/DeclSpec.h"
 #include "clang/Sema/EnterExpressionEvaluationContext.h"
@@ -54,9 +55,10 @@ IdentifierInfo *Parser::getSEHExceptKeyword() {
 
 Parser::Parser(Preprocessor &pp, Sema &actions, bool skipFunctionBodies)
     : PP(pp), PreferredType(pp.isCodeCompletionEnabled()), Actions(actions),
-      Diags(PP.getDiagnostics()), GreaterThanIsOperator(true),
-      ColonIsSacred(false), InMessageExpression(false),
-      TemplateParameterDepth(0), ParsingInObjCContainer(false) {
+      Diags(PP.getDiagnostics()), StackHandler(Diags),
+      GreaterThanIsOperator(true), ColonIsSacred(false),
+      InMessageExpression(false), TemplateParameterDepth(0),
+      ParsingInObjCContainer(false) {
   SkipFunctionBodies = pp.isCodeCompletionEnabled() || skipFunctionBodies;
   Tok.startToken();
   Tok.setKind(tok::eof);
diff --git a/clang/lib/Sema/ParsedAttr.cpp b/clang/lib/Sema/ParsedAttr.cpp
index eadc6ed6e6f58..b19a02b8c1a09 100644
--- a/clang/lib/Sema/ParsedAttr.cpp
+++ b/clang/lib/Sema/ParsedAttr.cpp
@@ -310,18 +310,13 @@ bool ParsedAttr::checkAtMostNumArgs(Sema &S, unsigned Num) const {
 }
 
 void clang::takeAndConcatenateAttrs(ParsedAttributes &First,
-                                    ParsedAttributes &Second,
-                                    ParsedAttributes &Result) {
-  // Note that takeAllFrom() puts the attributes at the beginning of the list,
-  // so to obtain the correct ordering, we add `Second`, then `First`.
-  Result.takeAllFrom(Second);
-  Result.takeAllFrom(First);
-  if (First.Range.getBegin().isValid())
-    Result.Range.setBegin(First.Range.getBegin());
-  else
-    Result.Range.setBegin(Second.Range.getBegin());
+                                    ParsedAttributes &&Second) {
+
+  First.takeAllAtEndFrom(Second);
+
+  if (!First.Range.getBegin().isValid())
+    First.Range.setBegin(Second.Range.getBegin());
+
   if (Second.Range.getEnd().isValid())
-    Result.Range.setEnd(Second.Range.getEnd());
-  else
-    Result.Range.setEnd(First.Range.getEnd());
+    First.Range.setEnd(Second.Range.getEnd());
 }
diff --git a/clang/test/Parser/parser_overflow.c b/clang/test/Parser/parser_overflow.c
index 9514e808550a4..53c79bc06d993 100644
--- a/clang/test/Parser/parser_overflow.c
+++ b/clang/test/Parser/parser_overflow.c
@@ -1,5 +1,5 @@
 // RUN: not %clang_cc1 %s -fsyntax-only -DHUGE 2>&1 | FileCheck %s
-// RUN: not %clang_cc1 %s -fsyntax-only 2>&1 | FileCheck %s
+// RUN: %clang_cc1 %s -fsyntax-only
 // RUN: not %clang_cc1 %s -fsyntax-only -fbracket-depth 299 2>&1 | FileCheck %s
 // RUN: %clang_cc1 %s -fsyntax-only -fbracket-depth 300
 // RUN: not %clang %s -fsyntax-only -fbracket-depth=299 2>&1 | FileCheck %s
@@ -15,5 +15,5 @@ void foo(void) {
 #endif
 }
 
-// CHECK: fatal error: bracket nesting level exceeded maximum of {{256|299}}
+// CHECK: fatal error: bracket nesting level exceeded maximum of {{2048|299}}
 // CHECK: note: use -fbracket-depth=N to increase maximum nesting level

@cor3ntin
Copy link
Contributor Author

Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems reasonable to me. All of the changes look sound. Good job finding room here!

@@ -169,6 +169,8 @@ Modified Compiler Flags
the behavior of ``-mtp`` in gcc. This changes the default behavior for ARM targets that provide the ``TPIDRURO`` register as this will be used instead of a call to the ``__aeabi_read_tp``.
Programs that use ``__aeabi_read_tp`` but do not use the ``TPIDRURO`` register must use ``-mtp=soft``. Fixes #123864

- The compiler flag `-fbracket-depth` default value is increased from 256 to 2048. (#GH94728)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, this is an aggressive change :)

Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems reasonable to me. All of the changes look sound. Good job finding room here!

@cor3ntin
Copy link
Contributor Author

Thanks @erichkeane. Let see if the bots scream

@cor3ntin cor3ntin merged commit baef6fa into llvm:main Mar 19, 2025
13 of 14 checks passed
Copy link
Collaborator

@shafik shafik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need to update this too, unless I missed something: https://clang.llvm.org/docs/UsersManual.html#controlling-implementation-limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Clang] Default expression nesting limit should be higher
4 participants