diff options
Diffstat (limited to 'clang/lib/Parse')
-rw-r--r-- | clang/lib/Parse/ParseDecl.cpp | 20 | ||||
-rw-r--r-- | clang/lib/Parse/ParseExpr.cpp | 4 | ||||
-rw-r--r-- | clang/lib/Parse/ParseExprCXX.cpp | 326 |
3 files changed, 338 insertions, 12 deletions
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 192c0e99e5a..178cb1b661c 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -6366,7 +6366,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D, ProhibitAttributes(FnAttrs); } else { if (Tok.isNot(tok::r_paren)) - ParseParameterDeclarationClause(D, FirstArgAttrs, ParamInfo, + ParseParameterDeclarationClause(D.getContext(), FirstArgAttrs, ParamInfo, EllipsisLoc); else if (RequiresArg) Diag(Tok, diag::err_argument_required_after_attribute); @@ -6584,9 +6584,9 @@ void Parser::ParseFunctionDeclaratorIdentifierList( /// after the opening parenthesis. This function will not parse a K&R-style /// identifier list. /// -/// D is the declarator being parsed. If FirstArgAttrs is non-null, then the -/// caller parsed those arguments immediately after the open paren - they should -/// be considered to be part of the first parameter. +/// DeclContext is the context of the declarator being parsed. If FirstArgAttrs +/// is non-null, then the caller parsed those attributes immediately after the +/// open paren - they should be considered to be part of the first parameter. /// /// After returning, ParamInfo will hold the parsed parameters. EllipsisLoc will /// be the location of the ellipsis, if any was parsed. @@ -6612,7 +6612,7 @@ void Parser::ParseFunctionDeclaratorIdentifierList( /// [C++11] attribute-specifier-seq parameter-declaration /// void Parser::ParseParameterDeclarationClause( - Declarator &D, + DeclaratorContext DeclaratorContext, ParsedAttributes &FirstArgAttrs, SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo, SourceLocation &EllipsisLoc) { @@ -6661,9 +6661,11 @@ void Parser::ParseParameterDeclarationClause( // "LambdaExprParameterContext", because we must accept either // 'declarator' or 'abstract-declarator' here. Declarator ParmDeclarator( - DS, D.getContext() == DeclaratorContext::LambdaExprContext - ? DeclaratorContext::LambdaExprParameterContext - : DeclaratorContext::PrototypeContext); + DS, DeclaratorContext == DeclaratorContext::RequiresExprContext + ? DeclaratorContext::RequiresExprContext + : DeclaratorContext == DeclaratorContext::LambdaExprContext + ? DeclaratorContext::LambdaExprParameterContext + : DeclaratorContext::PrototypeContext); ParseDeclarator(ParmDeclarator); // Parse GNU attributes, if present. @@ -6717,7 +6719,7 @@ void Parser::ParseParameterDeclarationClause( SourceLocation EqualLoc = Tok.getLocation(); // Parse the default argument - if (D.getContext() == DeclaratorContext::MemberContext) { + if (DeclaratorContext == DeclaratorContext::MemberContext) { // If we're inside a class definition, cache the tokens // corresponding to the default argument. We'll actually parse // them when we see the end of the class definition. diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 32dacbcc964..df8388926db 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -756,6 +756,7 @@ class CastExpressionIdValidator final : public CorrectionCandidateCallback { /// [C++11] user-defined-literal /// '(' expression ')' /// [C11] generic-selection +/// [C++2a] requires-expression /// '__func__' [C99 6.4.2.2] /// [GNU] '__FUNCTION__' /// [MS] '__FUNCDNAME__' @@ -1601,6 +1602,9 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, *NotPrimaryExpression = true; return ParseCXXDeleteExpression(false, Tok.getLocation()); + case tok::kw_requires: // [C++2a] requires-expression + return ParseRequiresExpression(); + case tok::kw_noexcept: { // [C++0x] 'noexcept' '(' expression ')' if (NotPrimaryExpression) *NotPrimaryExpression = true; diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 7d477d271dc..036eabb94dd 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -11,7 +11,9 @@ //===----------------------------------------------------------------------===// #include "clang/Parse/Parser.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/ExprCXX.h" #include "clang/Basic/PrettyStackTrace.h" #include "clang/Lex/LiteralSupport.h" #include "clang/Parse/ParseDiagnostic.h" @@ -1299,9 +1301,9 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer( Actions.RecordParsingTemplateParameterDepth( CurTemplateDepthTracker.getOriginalDepth()); - ParseParameterDeclarationClause(D, Attr, ParamInfo, EllipsisLoc); - - // For a generic lambda, each 'auto' within the parameter declaration + ParseParameterDeclarationClause(D.getContext(), Attr, ParamInfo, + EllipsisLoc); + // For a generic lambda, each 'auto' within the parameter declaration // clause creates a template type parameter, so increment the depth. // If we've parsed any explicit template parameters, then the depth will // have already been incremented. So we make sure that at most a single @@ -3255,6 +3257,324 @@ Parser::ParseCXXDeleteExpression(bool UseGlobal, SourceLocation Start) { return Actions.ActOnCXXDelete(Start, UseGlobal, ArrayDelete, Operand.get()); } +/// ParseRequiresExpression - Parse a C++2a requires-expression. +/// C++2a [expr.prim.req]p1 +/// A requires-expression provides a concise way to express requirements on +/// template arguments. A requirement is one that can be checked by name +/// lookup (6.4) or by checking properties of types and expressions. +/// +/// requires-expression: +/// 'requires' requirement-parameter-list[opt] requirement-body +/// +/// requirement-parameter-list: +/// '(' parameter-declaration-clause[opt] ')' +/// +/// requirement-body: +/// '{' requirement-seq '}' +/// +/// requirement-seq: +/// requirement +/// requirement-seq requirement +/// +/// requirement: +/// simple-requirement +/// type-requirement +/// compound-requirement +/// nested-requirement +ExprResult Parser::ParseRequiresExpression() { + assert(Tok.is(tok::kw_requires) && "Expected 'requires' keyword"); + SourceLocation RequiresKWLoc = ConsumeToken(); // Consume 'requires' + + llvm::SmallVector<ParmVarDecl *, 2> LocalParameterDecls; + if (Tok.is(tok::l_paren)) { + // requirement parameter list is present. + ParseScope LocalParametersScope(this, Scope::FunctionPrototypeScope | + Scope::DeclScope); + BalancedDelimiterTracker Parens(*this, tok::l_paren); + Parens.consumeOpen(); + if (!Tok.is(tok::r_paren)) { + ParsedAttributes FirstArgAttrs(getAttrFactory()); + SourceLocation EllipsisLoc; + llvm::SmallVector<DeclaratorChunk::ParamInfo, 2> LocalParameters; + DiagnosticErrorTrap Trap(Diags); + ParseParameterDeclarationClause(DeclaratorContext::RequiresExprContext, + FirstArgAttrs, LocalParameters, + EllipsisLoc); + if (EllipsisLoc.isValid()) + Diag(EllipsisLoc, diag::err_requires_expr_parameter_list_ellipsis); + for (auto &ParamInfo : LocalParameters) + LocalParameterDecls.push_back(cast<ParmVarDecl>(ParamInfo.Param)); + if (Trap.hasErrorOccurred()) + SkipUntil(tok::r_paren, StopBeforeMatch); + } + Parens.consumeClose(); + } + + BalancedDelimiterTracker Braces(*this, tok::l_brace); + if (Braces.expectAndConsume()) + return ExprError(); + + // Start of requirement list + llvm::SmallVector<concepts::Requirement *, 2> Requirements; + + // C++2a [expr.prim.req]p2 + // Expressions appearing within a requirement-body are unevaluated operands. + EnterExpressionEvaluationContext Ctx( + Actions, Sema::ExpressionEvaluationContext::Unevaluated); + + ParseScope BodyScope(this, Scope::DeclScope); + RequiresExprBodyDecl *Body = Actions.ActOnStartRequiresExpr( + RequiresKWLoc, LocalParameterDecls, getCurScope()); + + if (Tok.is(tok::r_brace)) { + // Grammar does not allow an empty body. + // requirement-body: + // { requirement-seq } + // requirement-seq: + // requirement + // requirement-seq requirement + Diag(Tok, diag::err_empty_requires_expr); + // Continue anyway and produce a requires expr with no requirements. + } else { + while (!Tok.is(tok::r_brace)) { + switch (Tok.getKind()) { + case tok::l_brace: { + // Compound requirement + // C++ [expr.prim.req.compound] + // compound-requirement: + // '{' expression '}' 'noexcept'[opt] + // return-type-requirement[opt] ';' + // return-type-requirement: + // trailing-return-type + // '->' cv-qualifier-seq[opt] constrained-parameter + // cv-qualifier-seq[opt] abstract-declarator[opt] + BalancedDelimiterTracker ExprBraces(*this, tok::l_brace); + ExprBraces.consumeOpen(); + ExprResult Expression = + Actions.CorrectDelayedTyposInExpr(ParseExpression()); + if (!Expression.isUsable()) { + ExprBraces.skipToEnd(); + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + if (ExprBraces.consumeClose()) + ExprBraces.skipToEnd(); + + concepts::Requirement *Req = nullptr; + SourceLocation NoexceptLoc; + TryConsumeToken(tok::kw_noexcept, NoexceptLoc); + if (Tok.is(tok::semi)) { + Req = Actions.ActOnCompoundRequirement(Expression.get(), NoexceptLoc); + if (Req) + Requirements.push_back(Req); + break; + } + if (!TryConsumeToken(tok::arrow)) + // User probably forgot the arrow, remind them and try to continue. + Diag(Tok, diag::err_requires_expr_missing_arrow) + << FixItHint::CreateInsertion(Tok.getLocation(), "->"); + // Try to parse a 'type-constraint' + CXXScopeSpec SS; + if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(), + /*EnteringContext=*/false, + /*MayBePseudoDestructor=*/nullptr, + // If this is not a type-constraint, + // then this scope-spec is part of + // the typename of a non-type + // template parameter + /*IsTypename=*/true, + /*LastII=*/nullptr, + // We won't find concepts in + // non-namespaces anyway, so might as + // well parse this correctly for + // possible type names. + /*OnlyNamespace=*/false, + /*SuppressDiagnostic=*/true)) { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + if (TryAnnotateTypeConstraint()) { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + if (!isTypeConstraintAnnotation()) { + Diag(Tok, diag::err_requires_expr_expected_type_constraint); + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + if (Tok.is(tok::annot_cxxscope)) + ConsumeAnnotationToken(); + + Req = Actions.ActOnCompoundRequirement( + Expression.get(), NoexceptLoc, SS, takeTemplateIdAnnotation(Tok), + TemplateParameterDepth); + ConsumeAnnotationToken(); + if (Req) + Requirements.push_back(Req); + break; + } + default: { + bool PossibleRequiresExprInSimpleRequirement = false; + if (Tok.is(tok::kw_requires)) { + auto IsNestedRequirement = [&] { + RevertingTentativeParsingAction TPA(*this); + ConsumeToken(); // 'requires' + if (Tok.is(tok::l_brace)) + // This is a requires expression + // requires (T t) { + // requires { t++; }; + // ... ^ + // } + return false; + if (Tok.is(tok::l_paren)) { + // This might be the parameter list of a requires expression + ConsumeParen(); + auto Res = TryParseParameterDeclarationClause(); + if (Res != TPResult::False) { + // Skip to the closing parenthesis + // FIXME: Don't traverse these tokens twice (here and in + // TryParseParameterDeclarationClause). + unsigned Depth = 1; + while (Depth != 0) { + if (Tok.is(tok::l_paren)) + Depth++; + else if (Tok.is(tok::r_paren)) + Depth--; + ConsumeAnyToken(); + } + // requires (T t) { + // requires () ? + // ... ^ + // - OR - + // requires (int x) ? + // ... ^ + // } + if (Tok.is(tok::l_brace)) + // requires (...) { + // ^ - a requires expression as a + // simple-requirement. + return false; + } + } + return true; + }; + if (IsNestedRequirement()) { + ConsumeToken(); + // Nested requirement + // C++ [expr.prim.req.nested] + // nested-requirement: + // 'requires' constraint-expression ';' + ExprResult ConstraintExpr = + Actions.CorrectDelayedTyposInExpr(ParseConstraintExpression()); + if (ConstraintExpr.isInvalid() || !ConstraintExpr.isUsable()) { + SkipUntil(tok::semi, tok::r_brace, + SkipUntilFlags::StopBeforeMatch); + break; + } + if (auto *Req = + Actions.ActOnNestedRequirement(ConstraintExpr.get())) + Requirements.push_back(Req); + else { + SkipUntil(tok::semi, tok::r_brace, + SkipUntilFlags::StopBeforeMatch); + break; + } + break; + } else + PossibleRequiresExprInSimpleRequirement = true; + } else if (Tok.is(tok::kw_typename)) { + // This might be 'typename T::value_type;' (a type requirement) or + // 'typename T::value_type{};' (a simple requirement). + TentativeParsingAction TPA(*this); + + // We need to consume the typename to allow 'requires { typename a; }' + SourceLocation TypenameKWLoc = ConsumeToken(); + if (TryAnnotateCXXScopeToken()) { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + CXXScopeSpec SS; + if (Tok.is(tok::annot_cxxscope)) { + Actions.RestoreNestedNameSpecifierAnnotation( + Tok.getAnnotationValue(), Tok.getAnnotationRange(), SS); + ConsumeAnnotationToken(); + } + + if (Tok.isOneOf(tok::identifier, tok::annot_template_id) && + !NextToken().isOneOf(tok::l_brace, tok::l_paren)) { + TPA.Commit(); + SourceLocation NameLoc = Tok.getLocation(); + IdentifierInfo *II = nullptr; + TemplateIdAnnotation *TemplateId = nullptr; + if (Tok.is(tok::identifier)) { + II = Tok.getIdentifierInfo(); + ConsumeToken(); + } else { + TemplateId = takeTemplateIdAnnotation(Tok); + ConsumeAnnotationToken(); + } + + if (auto *Req = Actions.ActOnTypeRequirement(TypenameKWLoc, SS, + NameLoc, II, + TemplateId)) { + Requirements.push_back(Req); + } + break; + } + TPA.Revert(); + } + // Simple requirement + // C++ [expr.prim.req.simple] + // simple-requirement: + // expression ';' + SourceLocation StartLoc = Tok.getLocation(); + ExprResult Expression = + Actions.CorrectDelayedTyposInExpr(ParseExpression()); + if (!Expression.isUsable()) { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + if (!Expression.isInvalid() && PossibleRequiresExprInSimpleRequirement) + Diag(StartLoc, diag::warn_requires_expr_in_simple_requirement) + << FixItHint::CreateInsertion(StartLoc, "requires"); + if (auto *Req = Actions.ActOnSimpleRequirement(Expression.get())) + Requirements.push_back(Req); + else { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + // User may have tried to put some compound requirement stuff here + if (Tok.is(tok::kw_noexcept)) { + Diag(Tok, diag::err_requires_expr_simple_requirement_noexcept) + << FixItHint::CreateInsertion(StartLoc, "{") + << FixItHint::CreateInsertion(Tok.getLocation(), "}"); + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + break; + } + } + if (ExpectAndConsumeSemi(diag::err_expected_semi_requirement)) { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + TryConsumeToken(tok::semi); + break; + } + } + if (Requirements.empty()) { + // Don't emit an empty requires expr here to avoid confusing the user with + // other diagnostics quoting an empty requires expression they never + // wrote. + Braces.consumeClose(); + Actions.ActOnFinishRequiresExpr(); + return ExprError(); + } + } + Braces.consumeClose(); + Actions.ActOnFinishRequiresExpr(); + return Actions.ActOnRequiresExpr(RequiresKWLoc, Body, LocalParameterDecls, + Requirements, Braces.getCloseLocation()); +} + static TypeTrait TypeTraitFromTokKind(tok::TokenKind kind) { switch (kind) { default: llvm_unreachable("Not a known type trait"); |