From 94cd71d65fe27cdde0c39416a0e2e709af98ed0c Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Mon, 30 Jun 2025 13:26:57 -0400 Subject: [PATCH 1/7] [C23] Fix typeof handling in enum declarations We have a parsing helper function which parses either a parenthesized expression or a parenthesized type name. This is used when parsing a unary operator such as sizeof, for example. The problem this solves is when that construct is ambiguous. Consider: enum E : typeof(int) { F }; After we've parsed the 'typeof', what ParseParenExpression() is responsible for is '(int) { F }' which looks like a compound literal expression when it's actually the parens and operand for 'typeof' followed by the enumerator list for the enumeration declaration. Then consider: sizeof (int){ 0 }; After we've parsed 'sizeof', ParseParenExpression is responsible for parsing something grammatically similar to the problematic case. The solution is to recognize that the expression form of 'typeof' is required to have parentheses. So we know the open and close parens that ParseParenExpression handles must be part of the grammar production for the operator, not part of the operand expression itself. Fixes #146351 --- clang/docs/ReleaseNotes.rst | 2 ++ clang/include/clang/Parse/Parser.h | 8 ++++++- clang/lib/Parse/ParseExpr.cpp | 34 +++++++++++++++++++++--------- clang/lib/Parse/Parser.cpp | 3 ++- clang/test/Parser/c2x-typeof.c | 10 +++++++++ 5 files changed, 45 insertions(+), 12 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 45b5f77545361..1b12061dad85b 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -304,6 +304,8 @@ C23 Feature Support which clarified how Clang is handling underspecified object declarations. - Clang now accepts single variadic parameter in type-name. It's a part of `WG14 N2975 `_ +- Fixed a bug with handling the type operand form of ``typeof`` when it is used + to specify a fixed underlying type for an enumeration. #GH146351 C11 Feature Support ^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index a47e23ffbd357..ced9613b9ab9b 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -4184,7 +4184,12 @@ class Parser : public CodeCompletionHandler { /// ParseParenExpression - This parses the unit that starts with a '(' token, /// based on what is allowed by ExprType. The actual thing parsed is returned /// in ExprType. If stopIfCastExpr is true, it will only return the parsed - /// type, not the parsed cast-expression. + /// type, not the parsed cast-expression. If ParenKnownToBeNonCast is true, + /// the initial open paren and its matching close paren are known to be part + /// of another grammar production and not part of the operand. e.g., the + /// typeof and typeof_unqual operators in C. If it is false, then the function + /// has to parse the parens to determine whether they're part of a cast or + /// compound literal expression rather than a parenthesized type. /// /// \verbatim /// primary-expression: [C99 6.5.1] @@ -4210,6 +4215,7 @@ class Parser : public CodeCompletionHandler { /// \endverbatim ExprResult ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr, bool isTypeCast, + bool ParenKnownToBeNonCast, ParsedType &CastTy, SourceLocation &RParenLoc); diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 3cf3d4ea7d705..c6731fca7bfa7 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -758,7 +758,8 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, ParsedType CastTy; SourceLocation RParenLoc; Res = ParseParenExpression(ParenExprType, false /*stopIfCastExr*/, - isTypeCast == TypeCastState::IsTypeCast, CastTy, + isTypeCast == TypeCastState::IsTypeCast, + false /*ParenKnownToBeNonCast*/, CastTy, RParenLoc); // FIXME: What should we do if a vector literal is followed by a @@ -2110,12 +2111,24 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok, // If it starts with a '(', we know that it is either a parenthesized // type-name, or it is a unary-expression that starts with a compound // literal, or starts with a primary-expression that is a parenthesized - // expression. + // expression. Most unary operators have an expression form without parens + // as part of the grammar for the operator, and a type form with the parens + // as part of the grammar for the operator. However, typeof and + // typeof_unqual require parens for both forms. This means that we *know* + // that the open and close parens cannot be part of a cast expression, + // which means we definitely are not parsing a compound literal expression. + // This disambiguates a case like enum E : typeof(int) { }; where we've + // parsed typeof and need to handle the (int){} tokens properly despite + // them looking like a compound literal, as in sizeof (int){}; where the + // parens could be part of a parenthesized type name or for a cast + // expression of some kind. + bool ParenKnownToBeNonCast = + OpTok.isOneOf(tok::kw_typeof, tok::kw_typeof_unqual); ParenParseOption ExprType = ParenParseOption::CastExpr; SourceLocation LParenLoc = Tok.getLocation(), RParenLoc; - Operand = ParseParenExpression(ExprType, true/*stopIfCastExpr*/, - false, CastTy, RParenLoc); + Operand = ParseParenExpression(ExprType, true /*stopIfCastExpr*/, false, + ParenKnownToBeNonCast, CastTy, RParenLoc); CastRange = SourceRange(LParenLoc, RParenLoc); // If ParseParenExpression parsed a '(typename)' sequence only, then this is @@ -2589,10 +2602,11 @@ bool Parser::tryParseOpenMPArrayShapingCastPart() { return !ErrorFound; } -ExprResult -Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr, - bool isTypeCast, ParsedType &CastTy, - SourceLocation &RParenLoc) { +ExprResult Parser::ParseParenExpression(ParenParseOption &ExprType, + bool stopIfCastExpr, bool isTypeCast, + bool ParenKnownToBeNonCast, + ParsedType &CastTy, + SourceLocation &RParenLoc) { assert(Tok.is(tok::l_paren) && "Not a paren expr!"); ColonProtectionRAIIObject ColonProtection(*this, false); BalancedDelimiterTracker T(*this, tok::l_paren); @@ -2747,7 +2761,7 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr, T.consumeClose(); ColonProtection.restore(); RParenLoc = T.getCloseLocation(); - if (Tok.is(tok::l_brace)) { + if (!ParenKnownToBeNonCast && Tok.is(tok::l_brace)) { ExprType = ParenParseOption::CompoundLiteral; TypeResult Ty; { @@ -2757,7 +2771,7 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr, return ParseCompoundLiteralExpression(Ty.get(), OpenLoc, RParenLoc); } - if (Tok.is(tok::l_paren)) { + if (!ParenKnownToBeNonCast && Tok.is(tok::l_paren)) { // This could be OpenCL vector Literals if (getLangOpts().OpenCL) { diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 18f399aca59e8..2f201cc49cf0b 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -1607,7 +1607,8 @@ ExprResult Parser::ParseAsmStringLiteral(bool ForAsmLabel) { EnterExpressionEvaluationContext ConstantEvaluated( Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated); AsmString = ParseParenExpression(ExprType, true /*stopIfCastExpr*/, false, - CastTy, RParenLoc); + false /*ParenKnownToBeNonCast*/, CastTy, + RParenLoc); if (!AsmString.isInvalid()) AsmString = Actions.ActOnConstantExpression(AsmString); diff --git a/clang/test/Parser/c2x-typeof.c b/clang/test/Parser/c2x-typeof.c index 9c836dfa6d823..245c127d71e03 100644 --- a/clang/test/Parser/c2x-typeof.c +++ b/clang/test/Parser/c2x-typeof.c @@ -42,3 +42,13 @@ _Static_assert(__builtin_offsetof(typeof(s), i) == 0); _Static_assert(__builtin_offsetof(typeof_unqual(struct S), i) == 0); _Static_assert(__builtin_offsetof(typeof_unqual(s), i) == 0); +// Show that typeof and typeof_unqual can be used in the underlying type of an +// enumeration even when given the type form. Note, this can look like a +// compound literal expression, which caused GH146351. +enum E3 : typeof(int) { ThirdZero }; // (int) {}; is not a compound literal! +enum E4 : typeof_unqual(int) { FourthZero }; // Same here + +// Ensure that this invalid construct is diagnosed instead of being treated +// as typeof((int){ 0 }). +typeof(int) { 0 } x; // expected-error {{a type specifier is required for all declarations}} \ + expected-error {{expected identifier or '('}} From e50a2af39de36bddf78e182e83ea546853480d6a Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Wed, 2 Jul 2025 09:32:59 -0400 Subject: [PATCH 2/7] Rename TypeCastState to TypoCorrectionBehavior; NFC --- clang/include/clang/Parse/Parser.h | 34 ++++--- clang/lib/Parse/ParseExpr.cpp | 148 +++++++++++++++-------------- clang/lib/Parse/ParseExprCXX.cpp | 2 +- clang/lib/Parse/ParseOpenMP.cpp | 11 ++- clang/lib/Parse/ParseTemplate.cpp | 4 +- 5 files changed, 106 insertions(+), 93 deletions(-) diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index ced9613b9ab9b..58d7b272e3b8e 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -101,8 +101,13 @@ enum class ObjCTypeQual { NumQuals }; -/// TypeCastState - State whether an expression is or may be a type cast. -enum class TypeCastState { NotTypeCast = 0, MaybeTypeCast, IsTypeCast }; +/// If a typo should be encountered, should typo correction suggest type names, +/// non type names, or both? +enum class TypoCorrectionTypeBehavior { + AllowNonTypes, + AllowTypes, + AllowBoth, +}; /// Control what ParseCastExpression will parse. enum class CastParseKind { AnyCastExpr = 0, UnaryExprOnly, PrimaryExprOnly }; @@ -3709,11 +3714,12 @@ class Parser : public CodeCompletionHandler { /// assignment-expression ...[opt] /// expression ',' assignment-expression ...[opt] /// \endverbatim - ExprResult - ParseExpression(TypeCastState isTypeCast = TypeCastState::NotTypeCast); + ExprResult ParseExpression(TypoCorrectionTypeBehavior CorrectionBehavior = + TypoCorrectionTypeBehavior::AllowNonTypes); ExprResult ParseConstantExpressionInExprEvalContext( - TypeCastState isTypeCast = TypeCastState::NotTypeCast); + TypoCorrectionTypeBehavior CorrectionBehavior = + TypoCorrectionTypeBehavior::AllowNonTypes); ExprResult ParseConstantExpression(); ExprResult ParseArrayBoundExpression(); ExprResult ParseCaseExpression(SourceLocation CaseLoc); @@ -3750,8 +3756,9 @@ class Parser : public CodeCompletionHandler { ExprResult ParseConstraintLogicalOrExpression(bool IsTrailingRequiresClause); /// Parse an expr that doesn't include (top-level) commas. - ExprResult ParseAssignmentExpression( - TypeCastState isTypeCast = TypeCastState::NotTypeCast); + ExprResult + ParseAssignmentExpression(TypoCorrectionTypeBehavior CorrectionBehavior = + TypoCorrectionTypeBehavior::AllowNonTypes); ExprResult ParseConditionalExpression(); @@ -4017,14 +4024,15 @@ class Parser : public CodeCompletionHandler { /// ExprResult ParseCastExpression(CastParseKind ParseKind, bool isAddressOfOperand, bool &NotCastExpr, - TypeCastState isTypeCast, + TypoCorrectionTypeBehavior CorrectionBehavior, + bool isVectorLiteral = false, + bool *NotPrimaryExpression = nullptr); + ExprResult ParseCastExpression(CastParseKind ParseKind, + bool isAddressOfOperand = false, + TypoCorrectionTypeBehavior CorrectionBehavior = + TypoCorrectionTypeBehavior::AllowNonTypes, bool isVectorLiteral = false, bool *NotPrimaryExpression = nullptr); - ExprResult - ParseCastExpression(CastParseKind ParseKind, bool isAddressOfOperand = false, - TypeCastState isTypeCast = TypeCastState::NotTypeCast, - bool isVectorLiteral = false, - bool *NotPrimaryExpression = nullptr); /// Returns true if the next token cannot start an expression. bool isNotExpressionStart(); diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index c6731fca7bfa7..593922efa51aa 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -43,8 +43,9 @@ #include using namespace clang; -ExprResult Parser::ParseExpression(TypeCastState isTypeCast) { - ExprResult LHS(ParseAssignmentExpression(isTypeCast)); +ExprResult +Parser::ParseExpression(TypoCorrectionTypeBehavior CorrectionBehavior) { + ExprResult LHS(ParseAssignmentExpression(CorrectionBehavior)); return ParseRHSOfBinaryExpression(LHS, prec::Comma); } @@ -71,7 +72,8 @@ Parser::ParseExpressionWithLeadingExtension(SourceLocation ExtLoc) { return ParseRHSOfBinaryExpression(LHS, prec::Comma); } -ExprResult Parser::ParseAssignmentExpression(TypeCastState isTypeCast) { +ExprResult Parser::ParseAssignmentExpression( + TypoCorrectionTypeBehavior CorrectionBehavior) { if (Tok.is(tok::code_completion)) { cutOffParsing(); Actions.CodeCompletion().CodeCompleteExpression( @@ -86,7 +88,7 @@ ExprResult Parser::ParseAssignmentExpression(TypeCastState isTypeCast) { ExprResult LHS = ParseCastExpression(CastParseKind::AnyCastExpr, - /*isAddressOfOperand=*/false, isTypeCast); + /*isAddressOfOperand=*/false, CorrectionBehavior); return ParseRHSOfBinaryExpression(LHS, prec::Assignment); } @@ -98,9 +100,9 @@ ExprResult Parser::ParseConditionalExpression() { return ExprError(); } - ExprResult LHS = ParseCastExpression(CastParseKind::AnyCastExpr, - /*isAddressOfOperand=*/false, - TypeCastState::NotTypeCast); + ExprResult LHS = ParseCastExpression( + CastParseKind::AnyCastExpr, + /*isAddressOfOperand=*/false, TypoCorrectionTypeBehavior::AllowNonTypes); return ParseRHSOfBinaryExpression(LHS, prec::Conditional); } @@ -116,14 +118,14 @@ Parser::ParseAssignmentExprWithObjCMessageExprStart(SourceLocation LBracLoc, return ParseRHSOfBinaryExpression(R, prec::Assignment); } -ExprResult -Parser::ParseConstantExpressionInExprEvalContext(TypeCastState isTypeCast) { +ExprResult Parser::ParseConstantExpressionInExprEvalContext( + TypoCorrectionTypeBehavior CorrectionBehavior) { assert(Actions.ExprEvalContexts.back().Context == Sema::ExpressionEvaluationContext::ConstantEvaluated && "Call this function only if your ExpressionEvaluationContext is " "already ConstantEvaluated"); - ExprResult LHS( - ParseCastExpression(CastParseKind::AnyCastExpr, false, isTypeCast)); + ExprResult LHS(ParseCastExpression(CastParseKind::AnyCastExpr, false, + CorrectionBehavior)); ExprResult Res(ParseRHSOfBinaryExpression(LHS, prec::Conditional)); return Actions.ActOnConstantExpression(Res); } @@ -135,7 +137,8 @@ ExprResult Parser::ParseConstantExpression() { // C++98 and C++11 have no such rule, but this is only a defect in C++98. EnterExpressionEvaluationContext ConstantEvaluated( Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated); - return ParseConstantExpressionInExprEvalContext(TypeCastState::NotTypeCast); + return ParseConstantExpressionInExprEvalContext( + TypoCorrectionTypeBehavior::AllowNonTypes); } ExprResult Parser::ParseArrayBoundExpression() { @@ -163,7 +166,8 @@ ExprResult Parser::ParseArrayBoundExpression() { break; Iter->InConditionallyConstantEvaluateContext = true; } - return ParseConstantExpressionInExprEvalContext(TypeCastState::NotTypeCast); + return ParseConstantExpressionInExprEvalContext( + TypoCorrectionTypeBehavior::AllowNonTypes); } ExprResult Parser::ParseCaseExpression(SourceLocation CaseLoc) { @@ -171,8 +175,9 @@ ExprResult Parser::ParseCaseExpression(SourceLocation CaseLoc) { Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated); Actions.currentEvaluationContext().IsCaseExpr = true; - ExprResult LHS(ParseCastExpression(CastParseKind::AnyCastExpr, false, - TypeCastState::NotTypeCast)); + ExprResult LHS( + ParseCastExpression(CastParseKind::AnyCastExpr, false, + TypoCorrectionTypeBehavior::AllowNonTypes)); ExprResult Res(ParseRHSOfBinaryExpression(LHS, prec::Conditional)); return Actions.ActOnCaseExpr(CaseLoc, Res); } @@ -194,11 +199,10 @@ Parser::ParseConstraintLogicalAndExpression(bool IsTrailingRequiresClause) { Actions, Sema::ExpressionEvaluationContext::Unevaluated); bool NotPrimaryExpression = false; auto ParsePrimary = [&]() { - ExprResult E = - ParseCastExpression(CastParseKind::PrimaryExprOnly, - /*isAddressOfOperand=*/false, - /*isTypeCast=*/TypeCastState::NotTypeCast, - /*isVectorLiteral=*/false, &NotPrimaryExpression); + ExprResult E = ParseCastExpression( + CastParseKind::PrimaryExprOnly, + /*isAddressOfOperand=*/false, TypoCorrectionTypeBehavior::AllowNonTypes, + /*isVectorLiteral=*/false, &NotPrimaryExpression); if (E.isInvalid()) return ExprError(); auto RecoverFromNonPrimary = [&] (ExprResult E, bool Note) { @@ -555,18 +559,14 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) { } } -ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, - bool isAddressOfOperand, - TypeCastState isTypeCast, - bool isVectorLiteral, - bool *NotPrimaryExpression) { +ExprResult +Parser::ParseCastExpression(CastParseKind ParseKind, bool isAddressOfOperand, + TypoCorrectionTypeBehavior CorrectionBehavior, + bool isVectorLiteral, bool *NotPrimaryExpression) { bool NotCastExpr; - ExprResult Res = ParseCastExpression(ParseKind, - isAddressOfOperand, - NotCastExpr, - isTypeCast, - isVectorLiteral, - NotPrimaryExpression); + ExprResult Res = ParseCastExpression(ParseKind, isAddressOfOperand, + NotCastExpr, CorrectionBehavior, + isVectorLiteral, NotPrimaryExpression); if (NotCastExpr) Diag(Tok, diag::err_expected_expression); return Res; @@ -713,12 +713,11 @@ ExprResult Parser::ParseBuiltinPtrauthTypeDiscriminator() { /*isType=*/true, Ty.get().getAsOpaquePtr(), SourceRange(Loc, EndLoc)); } -ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, - bool isAddressOfOperand, - bool &NotCastExpr, - TypeCastState isTypeCast, - bool isVectorLiteral, - bool *NotPrimaryExpression) { +ExprResult +Parser::ParseCastExpression(CastParseKind ParseKind, bool isAddressOfOperand, + bool &NotCastExpr, + TypoCorrectionTypeBehavior CorrectionBehavior, + bool isVectorLiteral, bool *NotPrimaryExpression) { ExprResult Res; tok::TokenKind SavedKind = Tok.getKind(); auto SavedType = PreferredType; @@ -757,10 +756,10 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, } ParsedType CastTy; SourceLocation RParenLoc; - Res = ParseParenExpression(ParenExprType, false /*stopIfCastExr*/, - isTypeCast == TypeCastState::IsTypeCast, - false /*ParenKnownToBeNonCast*/, CastTy, - RParenLoc); + Res = ParseParenExpression( + ParenExprType, false /*stopIfCastExr*/, + CorrectionBehavior == TypoCorrectionTypeBehavior::AllowTypes, + false /*ParenKnownToBeNonCast*/, CastTy, RParenLoc); // FIXME: What should we do if a vector literal is followed by a // postfix-expression suffix? Usually postfix operators are permitted on @@ -843,8 +842,9 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, case tok::annot_embed: { injectEmbedTokens(); - return ParseCastExpression(ParseKind, isAddressOfOperand, isTypeCast, - isVectorLiteral, NotPrimaryExpression); + return ParseCastExpression(ParseKind, isAddressOfOperand, + CorrectionBehavior, isVectorLiteral, + NotPrimaryExpression); } case tok::kw___super: @@ -853,8 +853,9 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, if (TryAnnotateTypeOrScopeToken()) return ExprError(); assert(Tok.isNot(tok::kw_decltype) && Tok.isNot(tok::kw___super)); - return ParseCastExpression(ParseKind, isAddressOfOperand, isTypeCast, - isVectorLiteral, NotPrimaryExpression); + return ParseCastExpression(ParseKind, isAddressOfOperand, + CorrectionBehavior, isVectorLiteral, + NotPrimaryExpression); case tok::identifier: ParseIdentifier: { // primary-expression: identifier @@ -874,8 +875,9 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, // indexing if (!TryAnnotateTypeOrScopeToken() && Tok.isOneOf(tok::annot_pack_indexing_type, tok::annot_cxxscope)) - return ParseCastExpression(ParseKind, isAddressOfOperand, isTypeCast, - isVectorLiteral, NotPrimaryExpression); + return ParseCastExpression(ParseKind, isAddressOfOperand, + CorrectionBehavior, isVectorLiteral, + NotPrimaryExpression); } // If this identifier was reverted from a token ID, and the next token @@ -887,9 +889,9 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, tok::TokenKind Kind; if (isRevertibleTypeTrait(II, &Kind)) { Tok.setKind(Kind); - return ParseCastExpression(ParseKind, isAddressOfOperand, - NotCastExpr, isTypeCast, - isVectorLiteral, NotPrimaryExpression); + return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr, + CorrectionBehavior, isVectorLiteral, + NotPrimaryExpression); } } @@ -900,9 +902,8 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, if (TryAnnotateTypeOrScopeToken()) return ExprError(); if (!Tok.is(tok::identifier)) - return ParseCastExpression(ParseKind, isAddressOfOperand, - NotCastExpr, isTypeCast, - isVectorLiteral, + return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr, + CorrectionBehavior, isVectorLiteral, NotPrimaryExpression); } } @@ -1001,8 +1002,10 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, Token Replacement; CastExpressionIdValidator Validator( /*Next=*/Tok, - /*AllowTypes=*/isTypeCast != TypeCastState::NotTypeCast, - /*AllowNonTypes=*/isTypeCast != TypeCastState::IsTypeCast); + /*AllowTypes=*/CorrectionBehavior != + TypoCorrectionTypeBehavior::AllowNonTypes, + /*AllowNonTypes=*/CorrectionBehavior != + TypoCorrectionTypeBehavior::AllowTypes); Validator.IsAddressOfOperand = isAddressOfOperand; if (Tok.isOneOf(tok::periodstar, tok::arrowstar)) { Validator.WantExpressionKeywords = false; @@ -1018,10 +1021,9 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, Tok.is(tok::r_paren) ? nullptr : &Replacement); if (!Res.isInvalid() && Res.isUnset()) { UnconsumeToken(Replacement); - return ParseCastExpression(ParseKind, isAddressOfOperand, - NotCastExpr, isTypeCast, - /*isVectorLiteral=*/false, - NotPrimaryExpression); + return ParseCastExpression( + ParseKind, isAddressOfOperand, NotCastExpr, CorrectionBehavior, + /*isVectorLiteral=*/false, NotPrimaryExpression); } Res = tryParseCXXPackIndexingExpression(Res); if (!Res.isInvalid() && Tok.is(tok::less)) @@ -1103,10 +1105,11 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, // One special case is implicitly handled here: if the preceding tokens are // an ambiguous cast expression, such as "(T())++", then we recurse to // determine whether the '++' is prefix or postfix. - Res = ParseCastExpression( - getLangOpts().CPlusPlus ? CastParseKind::UnaryExprOnly - : CastParseKind::AnyCastExpr, - /*isAddressOfOperand*/ false, NotCastExpr, TypeCastState::NotTypeCast); + Res = ParseCastExpression(getLangOpts().CPlusPlus + ? CastParseKind::UnaryExprOnly + : CastParseKind::AnyCastExpr, + /*isAddressOfOperand*/ false, NotCastExpr, + TypoCorrectionTypeBehavior::AllowNonTypes); if (NotCastExpr) { // If we return with NotCastExpr = true, we must not consume any tokens, // so put the token back where we found it. @@ -1367,7 +1370,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, return ExprError(); if (!Tok.is(tok::annot_cxxscope)) return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr, - isTypeCast, isVectorLiteral, + CorrectionBehavior, isVectorLiteral, NotPrimaryExpression); Token Next = NextToken(); @@ -1383,7 +1386,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, /*EnteringContext=*/false); AnnotateTemplateIdTokenAsType(SS, ImplicitTypenameContext::Yes); return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr, - isTypeCast, isVectorLiteral, + CorrectionBehavior, isVectorLiteral, NotPrimaryExpression); } } @@ -1401,8 +1404,8 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, // expression. CXXScopeSpec SS; AnnotateTemplateIdTokenAsType(SS, ImplicitTypenameContext::Yes); - return ParseCastExpression(ParseKind, isAddressOfOperand, - NotCastExpr, isTypeCast, isVectorLiteral, + return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr, + CorrectionBehavior, isVectorLiteral, NotPrimaryExpression); } @@ -1420,8 +1423,9 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, if (TryAnnotateTypeOrScopeToken()) return ExprError(); if (!Tok.is(tok::coloncolon)) - return ParseCastExpression(ParseKind, isAddressOfOperand, isTypeCast, - isVectorLiteral, NotPrimaryExpression); + return ParseCastExpression(ParseKind, isAddressOfOperand, + CorrectionBehavior, isVectorLiteral, + NotPrimaryExpression); // ::new -> [C++] new-expression // ::delete -> [C++] delete-expression @@ -2795,7 +2799,7 @@ ExprResult Parser::ParseParenExpression(ParenParseOption &ExprType, Result = ParseCastExpression( /*isUnaryExpression=*/CastParseKind::AnyCastExpr, /*isAddressOfOperand=*/false, - /*isTypeCast=*/TypeCastState::IsTypeCast, + TypoCorrectionTypeBehavior::AllowTypes, /*isVectorLiteral=*/true); if (!Result.isInvalid()) { @@ -2848,7 +2852,7 @@ ExprResult Parser::ParseParenExpression(ParenParseOption &ExprType, Result = ParseCastExpression( /*isUnaryExpression=*/CastParseKind::AnyCastExpr, /*isAddressOfOperand=*/false, - /*isTypeCast=*/TypeCastState::IsTypeCast); + TypoCorrectionTypeBehavior::AllowTypes); if (!Result.isInvalid()) { Result = Actions.ActOnCastExpr(getCurScope(), OpenLoc, DeclaratorInfo, CastTy, @@ -2916,7 +2920,7 @@ ExprResult Parser::ParseParenExpression(ParenParseOption &ExprType, } else { InMessageExpressionRAIIObject InMessage(*this, false); - Result = ParseExpression(TypeCastState::MaybeTypeCast); + Result = ParseExpression(TypoCorrectionTypeBehavior::AllowBoth); if (ExprType >= ParenParseOption::FoldExpr && isFoldOperator(Tok.getKind()) && NextToken().is(tok::ellipsis)) { ExprType = ParenParseOption::FoldExpr; diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 1ea0cf52933f6..7686ff73f827b 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -3607,7 +3607,7 @@ Parser::ParseCXXAmbiguousParenExpression(ParenParseOption &ExprType, Result = ParseCastExpression(CastParseKind::AnyCastExpr, false /*isAddressofOperand*/, NotCastExpr, // type-id has priority. - TypeCastState::IsTypeCast); + TypoCorrectionTypeBehavior::AllowTypes); } // If we parsed a cast-expression, it's really a type-id, otherwise it's diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index f694ae1d0d112..2c18f3e9306b6 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -3503,9 +3503,9 @@ ExprResult Parser::ParseOpenMPParensExpr(StringRef ClauseName, return ExprError(); SourceLocation ELoc = Tok.getLocation(); - ExprResult LHS(ParseCastExpression(CastParseKind::AnyCastExpr, - IsAddressOfOperand, - TypeCastState::NotTypeCast)); + ExprResult LHS( + ParseCastExpression(CastParseKind::AnyCastExpr, IsAddressOfOperand, + TypoCorrectionTypeBehavior::AllowNonTypes)); ExprResult Val(ParseRHSOfBinaryExpression(LHS, prec::Conditional)); Val = Actions.ActOnFinishFullExpr(Val.get(), ELoc, /*DiscardedValue*/ false); @@ -4036,8 +4036,9 @@ OMPClause *Parser::ParseOpenMPSingleExprWithArgClause(OpenMPDirectiveKind DKind, Kind == OMPC_num_threads; if (NeedAnExpression) { SourceLocation ELoc = Tok.getLocation(); - ExprResult LHS(ParseCastExpression(CastParseKind::AnyCastExpr, false, - TypeCastState::NotTypeCast)); + ExprResult LHS( + ParseCastExpression(CastParseKind::AnyCastExpr, false, + TypoCorrectionTypeBehavior::AllowNonTypes)); Val = ParseRHSOfBinaryExpression(LHS, prec::Conditional); Val = Actions.ActOnFinishFullExpr(Val.get(), ELoc, /*DiscardedValue*/ false); diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index a16dbe95b788d..9a04bf298c0ac 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -1318,8 +1318,8 @@ ParsedTemplateArgument Parser::ParseTemplateArgument() { if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) ExprArg = ParseBraceInitializer(); else - ExprArg = - ParseConstantExpressionInExprEvalContext(TypeCastState::MaybeTypeCast); + ExprArg = ParseConstantExpressionInExprEvalContext( + TypoCorrectionTypeBehavior::AllowBoth); if (ExprArg.isInvalid() || !ExprArg.get()) { return ParsedTemplateArgument(); } From c2b6a9c69f082e0045707ea2ecabeaea1fafa251 Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Wed, 2 Jul 2025 14:39:36 -0400 Subject: [PATCH 3/7] Change bool parameters into an enum --- clang/include/clang/Parse/Parser.h | 28 ++++++++++++++++-------- clang/lib/Parse/ParseExpr.cpp | 35 ++++++++++++++++-------------- clang/lib/Parse/Parser.cpp | 6 ++--- 3 files changed, 41 insertions(+), 28 deletions(-) diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 58d7b272e3b8e..647e65aa7fe48 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -121,6 +121,15 @@ enum class ParenParseOption { CastExpr // Also allow '(' type-name ')' }; +/// In a call to ParseParenExpression, are the initial parentheses part of an +/// operator that requires the parens be there (like typeof(int)) or could they +/// be something else, such as part of a compound literal or a sizeof +/// expression, etc. +enum class ParenExprKind { + PartOfOperator, // typeof(int) + Unknown, // sizeof(int) or sizeof (int)1.0f, or compound literal, etc +}; + /// Describes the behavior that should be taken for an __if_exists /// block. enum class IfExistsBehavior { @@ -4189,15 +4198,15 @@ class Parser : public CodeCompletionHandler { /// \endverbatim bool ParseSimpleExpressionList(SmallVectorImpl &Exprs); - /// ParseParenExpression - This parses the unit that starts with a '(' token, - /// based on what is allowed by ExprType. The actual thing parsed is returned - /// in ExprType. If stopIfCastExpr is true, it will only return the parsed - /// type, not the parsed cast-expression. If ParenKnownToBeNonCast is true, + /// This parses the unit that starts with a '(' token, based on what is + /// allowed by ExprType. The actual thing parsed is returned in ExprType. If + /// StopIfCastExpr is true, it will only return the parsed type, not the + /// parsed cast-expression. If ParenBehavior is ParenExprKind::PartOfOperator, /// the initial open paren and its matching close paren are known to be part /// of another grammar production and not part of the operand. e.g., the - /// typeof and typeof_unqual operators in C. If it is false, then the function - /// has to parse the parens to determine whether they're part of a cast or - /// compound literal expression rather than a parenthesized type. + /// typeof and typeof_unqual operators in C. Otherwise, the function has to + /// parse the parens to determine whether they're part of a cast or compound + /// literal expression rather than a parenthesized type. /// /// \verbatim /// primary-expression: [C99 6.5.1] @@ -4222,8 +4231,9 @@ class Parser : public CodeCompletionHandler { /// '(' '[' expression ']' { '[' expression ']' } cast-expression /// \endverbatim ExprResult ParseParenExpression(ParenParseOption &ExprType, - bool stopIfCastExpr, bool isTypeCast, - bool ParenKnownToBeNonCast, + bool StopIfCastExpr, + ParenExprKind ParenBehavior, + TypoCorrectionTypeBehavior CorrectionBehavior, ParsedType &CastTy, SourceLocation &RParenLoc); diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 593922efa51aa..8b34864aebd98 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -756,10 +756,9 @@ Parser::ParseCastExpression(CastParseKind ParseKind, bool isAddressOfOperand, } ParsedType CastTy; SourceLocation RParenLoc; - Res = ParseParenExpression( - ParenExprType, false /*stopIfCastExr*/, - CorrectionBehavior == TypoCorrectionTypeBehavior::AllowTypes, - false /*ParenKnownToBeNonCast*/, CastTy, RParenLoc); + Res = ParseParenExpression(ParenExprType, /*StopIfCastExr=*/false, + ParenExprKind::Unknown, CorrectionBehavior, + CastTy, RParenLoc); // FIXME: What should we do if a vector literal is followed by a // postfix-expression suffix? Usually postfix operators are permitted on @@ -2131,8 +2130,11 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok, ParenParseOption ExprType = ParenParseOption::CastExpr; SourceLocation LParenLoc = Tok.getLocation(), RParenLoc; - Operand = ParseParenExpression(ExprType, true /*stopIfCastExpr*/, false, - ParenKnownToBeNonCast, CastTy, RParenLoc); + Operand = ParseParenExpression( + ExprType, /*StopIfCastExr=*/true, + ParenKnownToBeNonCast ? ParenExprKind::PartOfOperator + : ParenExprKind::Unknown, + TypoCorrectionTypeBehavior::AllowBoth, CastTy, RParenLoc); CastRange = SourceRange(LParenLoc, RParenLoc); // If ParseParenExpression parsed a '(typename)' sequence only, then this is @@ -2606,11 +2608,11 @@ bool Parser::tryParseOpenMPArrayShapingCastPart() { return !ErrorFound; } -ExprResult Parser::ParseParenExpression(ParenParseOption &ExprType, - bool stopIfCastExpr, bool isTypeCast, - bool ParenKnownToBeNonCast, - ParsedType &CastTy, - SourceLocation &RParenLoc) { +ExprResult +Parser::ParseParenExpression(ParenParseOption &ExprType, bool StopIfCastExpr, + ParenExprKind ParenBehavior, + TypoCorrectionTypeBehavior CorrectionBehavior, + ParsedType &CastTy, SourceLocation &RParenLoc) { assert(Tok.is(tok::l_paren) && "Not a paren expr!"); ColonProtectionRAIIObject ColonProtection(*this, false); BalancedDelimiterTracker T(*this, tok::l_paren); @@ -2732,7 +2734,7 @@ ExprResult Parser::ParseParenExpression(ParenParseOption &ExprType, // in which case we should treat it as type-id. // if stopIfCastExpr is false, we need to determine the context past the // parens, so we defer to ParseCXXAmbiguousParenExpression for that. - if (isAmbiguousTypeId && !stopIfCastExpr) { + if (isAmbiguousTypeId && !StopIfCastExpr) { ExprResult res = ParseCXXAmbiguousParenExpression(ExprType, CastTy, T, ColonProtection); RParenLoc = T.getCloseLocation(); @@ -2765,7 +2767,7 @@ ExprResult Parser::ParseParenExpression(ParenParseOption &ExprType, T.consumeClose(); ColonProtection.restore(); RParenLoc = T.getCloseLocation(); - if (!ParenKnownToBeNonCast && Tok.is(tok::l_brace)) { + if (ParenBehavior == ParenExprKind::Unknown && Tok.is(tok::l_brace)) { ExprType = ParenParseOption::CompoundLiteral; TypeResult Ty; { @@ -2775,7 +2777,7 @@ ExprResult Parser::ParseParenExpression(ParenParseOption &ExprType, return ParseCompoundLiteralExpression(Ty.get(), OpenLoc, RParenLoc); } - if (!ParenKnownToBeNonCast && Tok.is(tok::l_paren)) { + if (ParenBehavior == ParenExprKind::Unknown && Tok.is(tok::l_paren)) { // This could be OpenCL vector Literals if (getLangOpts().OpenCL) { @@ -2826,7 +2828,7 @@ ExprResult Parser::ParseParenExpression(ParenParseOption &ExprType, // Note that this doesn't parse the subsequent cast-expression, it just // returns the parsed type to the callee. - if (stopIfCastExpr) { + if (StopIfCastExpr) { TypeResult Ty; { InMessageExpressionRAIIObject InMessage(*this, false); @@ -2868,7 +2870,8 @@ ExprResult Parser::ParseParenExpression(ParenParseOption &ExprType, isFoldOperator(NextToken().getKind())) { ExprType = ParenParseOption::FoldExpr; return ParseFoldExpression(ExprResult(), T); - } else if (isTypeCast) { + } else if (CorrectionBehavior == TypoCorrectionTypeBehavior::AllowTypes) { + // FIXME: This should not be predicated on typo correction behavior. // Parse the expression-list. InMessageExpressionRAIIObject InMessage(*this, false); ExprVector ArgExprs; diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 2f201cc49cf0b..8834bf80c4016 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -1606,9 +1606,9 @@ ExprResult Parser::ParseAsmStringLiteral(bool ForAsmLabel) { EnterExpressionEvaluationContext ConstantEvaluated( Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated); - AsmString = ParseParenExpression(ExprType, true /*stopIfCastExpr*/, false, - false /*ParenKnownToBeNonCast*/, CastTy, - RParenLoc); + AsmString = ParseParenExpression( + ExprType, /*StopIfCastExr=*/true, ParenExprKind::Unknown, + TypoCorrectionTypeBehavior::AllowBoth, CastTy, RParenLoc); if (!AsmString.isInvalid()) AsmString = Actions.ActOnConstantExpression(AsmString); From 77112d3c71533123a9c51496a3420eb88099fb4f Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Thu, 3 Jul 2025 07:39:45 -0400 Subject: [PATCH 4/7] Fix formatting; NFC --- clang/include/clang/Parse/Parser.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 647e65aa7fe48..683934321a449 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -127,7 +127,7 @@ enum class ParenParseOption { /// expression, etc. enum class ParenExprKind { PartOfOperator, // typeof(int) - Unknown, // sizeof(int) or sizeof (int)1.0f, or compound literal, etc + Unknown, // sizeof(int) or sizeof (int)1.0f, or compound literal, etc }; /// Describes the behavior that should be taken for an __if_exists From d0d30cab3a15673b95511efbd9412af061f6aae6 Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Thu, 3 Jul 2025 08:13:28 -0400 Subject: [PATCH 5/7] Update CastExpressionIdValidator; NFC --- clang/lib/Parse/ParseExpr.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 8b34864aebd98..ca9714676308b 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -575,9 +575,13 @@ Parser::ParseCastExpression(CastParseKind ParseKind, bool isAddressOfOperand, namespace { class CastExpressionIdValidator final : public CorrectionCandidateCallback { public: - CastExpressionIdValidator(Token Next, bool AllowTypes, bool AllowNonTypes) - : NextToken(Next), AllowNonTypes(AllowNonTypes) { - WantTypeSpecifiers = WantFunctionLikeCasts = AllowTypes; + CastExpressionIdValidator(Token Next, + TypoCorrectionTypeBehavior CorrectionBehavior) + : NextToken(Next) { + WantTypeSpecifiers = WantFunctionLikeCasts = + (CorrectionBehavior != TypoCorrectionTypeBehavior::AllowNonTypes); + AllowNonTypes = + (CorrectionBehavior != TypoCorrectionTypeBehavior::AllowTypes); } bool ValidateCandidate(const TypoCorrection &candidate) override { @@ -999,12 +1003,7 @@ Parser::ParseCastExpression(CastParseKind ParseKind, bool isAddressOfOperand, CXXScopeSpec ScopeSpec; SourceLocation TemplateKWLoc; Token Replacement; - CastExpressionIdValidator Validator( - /*Next=*/Tok, - /*AllowTypes=*/CorrectionBehavior != - TypoCorrectionTypeBehavior::AllowNonTypes, - /*AllowNonTypes=*/CorrectionBehavior != - TypoCorrectionTypeBehavior::AllowTypes); + CastExpressionIdValidator Validator(Tok, CorrectionBehavior); Validator.IsAddressOfOperand = isAddressOfOperand; if (Tok.isOneOf(tok::periodstar, tok::arrowstar)) { Validator.WantExpressionKeywords = false; From ed956f831a1f5e68997ca92a40a0251f01c1a87d Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Thu, 3 Jul 2025 09:14:27 -0400 Subject: [PATCH 6/7] Maybe make clang-format on CI happy --- clang/lib/Parse/ParseExpr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index ca9714676308b..bc238a9517a37 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -574,7 +574,7 @@ Parser::ParseCastExpression(CastParseKind ParseKind, bool isAddressOfOperand, namespace { class CastExpressionIdValidator final : public CorrectionCandidateCallback { - public: +public: CastExpressionIdValidator(Token Next, TypoCorrectionTypeBehavior CorrectionBehavior) : NextToken(Next) { From bdcc24e998edc745ec30964293515173cc61be65 Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Thu, 3 Jul 2025 12:41:45 -0400 Subject: [PATCH 7/7] Add a test for Objective-C --- clang/test/Parser/c23-typeof.m | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 clang/test/Parser/c23-typeof.m diff --git a/clang/test/Parser/c23-typeof.m b/clang/test/Parser/c23-typeof.m new file mode 100644 index 0000000000000..e6a8dc918dbb1 --- /dev/null +++ b/clang/test/Parser/c23-typeof.m @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 -verify -std=c23 -fblocks -Wno-unused %s +// expected-no-diagnostics + +void f() { + ^ typeof((void)0) {}; // Ok + ^ typeof(void) {}; // Ok +}