C2::StmtResult C2Sema::ActOnIfStmt(SourceLocation ifLoc, Stmt* condition, StmtResult thenStmt, SourceLocation elseLoc, StmtResult elseStmt) { #ifdef SEMA_DEBUG std::cerr << COL_SEMA"SEMA: if statement at "; ifLoc.dump(SourceMgr); std::cerr << ANSI_NORMAL"\n"; #endif return StmtResult(new IfStmt(ifLoc, condition, thenStmt.get(), elseLoc, elseStmt.get())); }
static bool actOnCoroutineBodyStart(Sema &S, Scope *SC, SourceLocation KWLoc, StringRef Keyword) { if (!checkCoroutineContext(S, KWLoc, Keyword)) return false; auto *ScopeInfo = S.getCurFunction(); assert(ScopeInfo->CoroutinePromise); // If we have existing coroutine statements then we have already built // the initial and final suspend points. if (!ScopeInfo->NeedsCoroutineSuspends) return true; ScopeInfo->setNeedsCoroutineSuspends(false); auto *Fn = cast<FunctionDecl>(S.CurContext); SourceLocation Loc = Fn->getLocation(); // Build the initial suspend point auto buildSuspends = [&](StringRef Name) mutable -> StmtResult { ExprResult Suspend = buildPromiseCall(S, ScopeInfo->CoroutinePromise, Loc, Name, None); if (Suspend.isInvalid()) return StmtError(); Suspend = buildOperatorCoawaitCall(S, SC, Loc, Suspend.get()); if (Suspend.isInvalid()) return StmtError(); Suspend = S.BuildResolvedCoawaitExpr(Loc, Suspend.get(), /*IsImplicit*/ true); Suspend = S.ActOnFinishFullExpr(Suspend.get()); if (Suspend.isInvalid()) { S.Diag(Loc, diag::note_coroutine_promise_suspend_implicitly_required) << ((Name == "initial_suspend") ? 0 : 1); S.Diag(KWLoc, diag::note_declared_coroutine_here) << Keyword; return StmtError(); } return cast<Stmt>(Suspend.get()); }; StmtResult InitSuspend = buildSuspends("initial_suspend"); if (InitSuspend.isInvalid()) return true; StmtResult FinalSuspend = buildSuspends("final_suspend"); if (FinalSuspend.isInvalid()) return true; ScopeInfo->setCoroutineSuspends(InitSuspend.get(), FinalSuspend.get()); return true; }
bool CoroutineStmtBuilder::makeOnFallthrough() { assert(!IsPromiseDependentType && "cannot make statement while the promise type is dependent"); // [dcl.fct.def.coroutine]/4 // The unqualified-ids 'return_void' and 'return_value' are looked up in // the scope of class P. If both are found, the program is ill-formed. bool HasRVoid, HasRValue; LookupResult LRVoid = lookupMember(S, "return_void", PromiseRecordDecl, Loc, HasRVoid); LookupResult LRValue = lookupMember(S, "return_value", PromiseRecordDecl, Loc, HasRValue); StmtResult Fallthrough; if (HasRVoid && HasRValue) { // FIXME Improve this diagnostic S.Diag(FD.getLocation(), diag::err_coroutine_promise_incompatible_return_functions) << PromiseRecordDecl; S.Diag(LRVoid.getRepresentativeDecl()->getLocation(), diag::note_member_first_declared_here) << LRVoid.getLookupName(); S.Diag(LRValue.getRepresentativeDecl()->getLocation(), diag::note_member_first_declared_here) << LRValue.getLookupName(); return false; } else if (!HasRVoid && !HasRValue) { // FIXME: The PDTS currently specifies this case as UB, not ill-formed. // However we still diagnose this as an error since until the PDTS is fixed. S.Diag(FD.getLocation(), diag::err_coroutine_promise_requires_return_function) << PromiseRecordDecl; S.Diag(PromiseRecordDecl->getLocation(), diag::note_defined_here) << PromiseRecordDecl; return false; } else if (HasRVoid) { // If the unqualified-id return_void is found, flowing off the end of a // coroutine is equivalent to a co_return with no operand. Otherwise, // flowing off the end of a coroutine results in undefined behavior. Fallthrough = S.BuildCoreturnStmt(FD.getLocation(), nullptr, /*IsImplicit*/false); Fallthrough = S.ActOnFinishFullStmt(Fallthrough.get()); if (Fallthrough.isInvalid()) return false; } this->OnFallthrough = Fallthrough.get(); return true; }
StmtResult Parser::HandlePragmaCaptured() { assert(Tok.is(tok::annot_pragma_captured)); ConsumeToken(); if (Tok.isNot(tok::l_brace)) { PP.Diag(Tok, diag::err_expected_lbrace); return StmtError(); } SourceLocation Loc = Tok.getLocation(); ParseScope CapturedRegionScope(this, Scope::FnScope | Scope::DeclScope); Actions.ActOnCapturedRegionStart(Loc, getCurScope(), CR_Default); StmtResult R = ParseCompoundStatement(); CapturedRegionScope.Exit(); if (R.isInvalid()) { Actions.ActOnCapturedRegionError(); return StmtError(); } return Actions.ActOnCapturedRegionEnd(R.get()); }
C2::StmtResult C2Sema::ActOnDoStmt(SourceLocation loc, ExprResult Cond, StmtResult Then) { #ifdef SEMA_DEBUG std::cerr << COL_SEMA"SEMA: do statement at "; loc.dump(SourceMgr); std::cerr << ANSI_NORMAL"\n"; #endif return StmtResult(new DoStmt(loc, Cond.get(), Then.get())); }
StmtResult Sema::ActOnCoreturnStmt(SourceLocation Loc, Expr *E) { auto *Context = checkCoroutineContext(*this, Loc, "co_return"); StmtResult Res = StmtError(); if (Context && !Res.isInvalid()) Context->CoroutineStmts.push_back(Res.get()); return Res; }
bool CoroutineStmtBuilder::makePromiseStmt() { // Form a declaration statement for the promise declaration, so that AST // visitors can more easily find it. StmtResult PromiseStmt = S.ActOnDeclStmt(S.ConvertDeclToDeclGroup(Fn.CoroutinePromise), Loc, Loc); if (PromiseStmt.isInvalid()) return false; this->Promise = PromiseStmt.get(); return true; }
bool CoroutineStmtBuilder::makeReturnOnAllocFailure() { assert(!IsPromiseDependentType && "cannot make statement while the promise type is dependent"); // [dcl.fct.def.coroutine]/8 // The unqualified-id get_return_object_on_allocation_failure is looked up in // the scope of class P by class member access lookup (3.4.5). ... // If an allocation function returns nullptr, ... the coroutine return value // is obtained by a call to ... get_return_object_on_allocation_failure(). DeclarationName DN = S.PP.getIdentifierInfo("get_return_object_on_allocation_failure"); LookupResult Found(S, DN, Loc, Sema::LookupMemberName); if (!S.LookupQualifiedName(Found, PromiseRecordDecl)) return true; CXXScopeSpec SS; ExprResult DeclNameExpr = S.BuildDeclarationNameExpr(SS, Found, /*NeedsADL=*/false); if (DeclNameExpr.isInvalid()) return false; if (!diagReturnOnAllocFailure(S, DeclNameExpr.get(), PromiseRecordDecl, Fn)) return false; ExprResult ReturnObjectOnAllocationFailure = S.ActOnCallExpr(nullptr, DeclNameExpr.get(), Loc, {}, Loc); if (ReturnObjectOnAllocationFailure.isInvalid()) return false; StmtResult ReturnStmt = S.BuildReturnStmt(Loc, ReturnObjectOnAllocationFailure.get()); if (ReturnStmt.isInvalid()) { S.Diag(Found.getFoundDecl()->getLocation(), diag::note_member_declared_here) << DN; S.Diag(Fn.FirstCoroutineStmtLoc, diag::note_declared_coroutine_here) << Fn.getFirstCoroutineStmtKeyword(); return false; } this->ReturnStmtOnAllocFailure = ReturnStmt.get(); return true; }
void Parser::CheckStmtOrder(SourceLocation Loc, StmtResult SR) { auto S = SR.get(); if(SR.isUsable()) { if(Actions.InsideWhereConstruct(S)) Actions.CheckValidWhereStmtPart(S); } if(PrevStmtWasSelectCase) { PrevStmtWasSelectCase = false; if(SR.isUsable() && (isa<SelectionCase>(S) || (isa<ConstructPartStmt>(S) && cast<ConstructPartStmt>(S)->getConstructStmtClass() == ConstructPartStmt::EndSelectStmtClass))) return; Diag.Report(SR.isUsable()? S->getLocation() : Loc, diag::err_expected_case_or_end_select); } if(SR.isUsable() && isa<SelectCaseStmt>(S)) PrevStmtWasSelectCase = true; }
void Fix(CompoundStmt* CS) { if (!CS->size()) return; typedef llvm::SmallVector<Stmt*, 32> Statements; Statements Stmts; Stmts.append(CS->body_begin(), CS->body_end()); for (Statements::iterator I = Stmts.begin(); I != Stmts.end(); ++I) { if (!TraverseStmt(*I) && !m_HandledDecls.count(m_FoundDRE->getDecl())) { Sema::DeclGroupPtrTy VDPtrTy = m_Sema->ConvertDeclToDeclGroup(m_FoundDRE->getDecl()); StmtResult DS = m_Sema->ActOnDeclStmt(VDPtrTy, m_FoundDRE->getLocStart(), m_FoundDRE->getLocEnd()); assert(!DS.isInvalid() && "Invalid DeclStmt."); I = Stmts.insert(I, DS.get()); m_HandledDecls.insert(m_FoundDRE->getDecl()); } } CS->setStmts(m_Sema->getASTContext(), Stmts.data(), Stmts.size()); }
bool CoroutineStmtBuilder::makeParamMoves() { for (auto *paramDecl : FD.parameters()) { auto Ty = paramDecl->getType(); if (Ty->isDependentType()) continue; // No need to copy scalars, llvm will take care of them. if (Ty->getAsCXXRecordDecl()) { if (!paramDecl->getIdentifier()) continue; ExprResult ParamRef = S.BuildDeclRefExpr(paramDecl, paramDecl->getType(), ExprValueKind::VK_LValue, Loc); // FIXME: scope? if (ParamRef.isInvalid()) return false; Expr *RCast = castForMoving(S, ParamRef.get()); auto D = buildVarDecl(S, Loc, Ty, paramDecl->getIdentifier()->getName()); S.AddInitializerToDecl(D, RCast, /*DirectInit=*/true); // Convert decl to a statement. StmtResult Stmt = S.ActOnDeclStmt(S.ConvertDeclToDeclGroup(D), Loc, Loc); if (Stmt.isInvalid()) return false; ParamMovesVector.push_back(Stmt.get()); } } // Convert to ArrayRef in CtorArgs structure that builder inherits from. ParamMoves = ParamMovesVector; return true; }
/// \brief Parsing of declarative or executable OpenMP directives. /// /// threadprivate-directive: /// annot_pragma_openmp 'threadprivate' simple-variable-list /// annot_pragma_openmp_end /// /// executable-directive: /// annot_pragma_openmp 'parallel' | 'simd' | 'for' | 'sections' | /// 'section' | 'single' | 'master' | 'critical' [ '(' <name> ')' ] | /// 'parallel for' | 'parallel sections' | 'task' | 'taskyield' | /// 'barrier' | 'taskwait' | 'flush' {clause} annot_pragma_openmp_end /// StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(bool StandAloneAllowed) { assert(Tok.is(tok::annot_pragma_openmp) && "Not an OpenMP directive!"); ParenBraceBracketBalancer BalancerRAIIObj(*this); SmallVector<Expr *, 5> Identifiers; SmallVector<OMPClause *, 5> Clauses; SmallVector<llvm::PointerIntPair<OMPClause *, 1, bool>, OMPC_unknown + 1> FirstClauses(OMPC_unknown + 1); unsigned ScopeFlags = Scope::FnScope | Scope::DeclScope | Scope::OpenMPDirectiveScope; SourceLocation Loc = ConsumeToken(), EndLoc; auto DKind = ParseOpenMPDirectiveKind(*this); // Name of critical directive. DeclarationNameInfo DirName; StmtResult Directive = StmtError(); bool HasAssociatedStatement = true; bool FlushHasClause = false; switch (DKind) { case OMPD_threadprivate: ConsumeToken(); if (!ParseOpenMPSimpleVarList(OMPD_threadprivate, Identifiers, false)) { // The last seen token is annot_pragma_openmp_end - need to check for // extra tokens. if (Tok.isNot(tok::annot_pragma_openmp_end)) { Diag(Tok, diag::warn_omp_extra_tokens_at_eol) << getOpenMPDirectiveName(OMPD_threadprivate); SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch); } DeclGroupPtrTy Res = Actions.ActOnOpenMPThreadprivateDirective(Loc, Identifiers); Directive = Actions.ActOnDeclStmt(Res, Loc, Tok.getLocation()); } SkipUntil(tok::annot_pragma_openmp_end); break; case OMPD_flush: if (PP.LookAhead(0).is(tok::l_paren)) { FlushHasClause = true; // Push copy of the current token back to stream to properly parse // pseudo-clause OMPFlushClause. PP.EnterToken(Tok); } case OMPD_taskyield: case OMPD_barrier: case OMPD_taskwait: if (!StandAloneAllowed) { Diag(Tok, diag::err_omp_immediate_directive) << getOpenMPDirectiveName(DKind); } HasAssociatedStatement = false; // Fall through for further analysis. case OMPD_parallel: case OMPD_simd: case OMPD_for: case OMPD_sections: case OMPD_single: case OMPD_section: case OMPD_master: case OMPD_critical: case OMPD_parallel_for: case OMPD_parallel_sections: case OMPD_task: { ConsumeToken(); // Parse directive name of the 'critical' directive if any. if (DKind == OMPD_critical) { BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end); if (!T.consumeOpen()) { if (Tok.isAnyIdentifier()) { DirName = DeclarationNameInfo(Tok.getIdentifierInfo(), Tok.getLocation()); ConsumeAnyToken(); } else { Diag(Tok, diag::err_omp_expected_identifier_for_critical); } T.consumeClose(); } } if (isOpenMPLoopDirective(DKind)) ScopeFlags |= Scope::OpenMPLoopDirectiveScope; if (isOpenMPSimdDirective(DKind)) ScopeFlags |= Scope::OpenMPSimdDirectiveScope; ParseScope OMPDirectiveScope(this, ScopeFlags); Actions.StartOpenMPDSABlock(DKind, DirName, Actions.getCurScope(), Loc); while (Tok.isNot(tok::annot_pragma_openmp_end)) { OpenMPClauseKind CKind = Tok.isAnnotation() ? OMPC_unknown : FlushHasClause ? OMPC_flush : getOpenMPClauseKind(PP.getSpelling(Tok)); FlushHasClause = false; OMPClause *Clause = ParseOpenMPClause(DKind, CKind, !FirstClauses[CKind].getInt()); FirstClauses[CKind].setInt(true); if (Clause) { FirstClauses[CKind].setPointer(Clause); Clauses.push_back(Clause); } // Skip ',' if any. if (Tok.is(tok::comma)) ConsumeToken(); } // End location of the directive. EndLoc = Tok.getLocation(); // Consume final annot_pragma_openmp_end. ConsumeToken(); StmtResult AssociatedStmt; bool CreateDirective = true; if (HasAssociatedStatement) { // The body is a block scope like in Lambdas and Blocks. Sema::CompoundScopeRAII CompoundScope(Actions); Actions.ActOnOpenMPRegionStart(DKind, getCurScope()); Actions.ActOnStartOfCompoundStmt(); // Parse statement AssociatedStmt = ParseStatement(); Actions.ActOnFinishOfCompoundStmt(); if (!AssociatedStmt.isUsable()) { Actions.ActOnCapturedRegionError(); CreateDirective = false; } else { AssociatedStmt = Actions.ActOnCapturedRegionEnd(AssociatedStmt.get()); CreateDirective = AssociatedStmt.isUsable(); } } if (CreateDirective) Directive = Actions.ActOnOpenMPExecutableDirective( DKind, DirName, Clauses, AssociatedStmt.get(), Loc, EndLoc); // Exit scope. Actions.EndOpenMPDSABlock(Directive.get()); OMPDirectiveScope.Exit(); break; } case OMPD_unknown: Diag(Tok, diag::err_omp_unknown_directive); SkipUntil(tok::annot_pragma_openmp_end); break; } return Directive; }
/// \brief Parsing of declarative or executable OpenMP directives. /// /// threadprivate-directive: /// annot_pragma_openmp 'threadprivate' simple-variable-list /// annot_pragma_openmp_end /// /// parallel-directive: /// annot_pragma_openmp 'parallel' {clause} annot_pragma_openmp_end /// StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective() { assert(Tok.is(tok::annot_pragma_openmp) && "Not an OpenMP directive!"); ParenBraceBracketBalancer BalancerRAIIObj(*this); SmallVector<Expr *, 5> Identifiers; SmallVector<OMPClause *, 5> Clauses; SmallVector<llvm::PointerIntPair<OMPClause *, 1, bool>, NUM_OPENMP_CLAUSES> FirstClauses(NUM_OPENMP_CLAUSES); const unsigned ScopeFlags = Scope::FnScope | Scope::DeclScope | Scope::OpenMPDirectiveScope; SourceLocation Loc = ConsumeToken(), EndLoc; OpenMPDirectiveKind DKind = Tok.isAnnotation() ? OMPD_unknown : getOpenMPDirectiveKind(PP.getSpelling(Tok)); // Name of critical directive. DeclarationNameInfo DirName; StmtResult Directive = StmtError(); switch (DKind) { case OMPD_threadprivate: ConsumeToken(); if (!ParseOpenMPSimpleVarList(OMPD_threadprivate, Identifiers, false)) { // The last seen token is annot_pragma_openmp_end - need to check for // extra tokens. if (Tok.isNot(tok::annot_pragma_openmp_end)) { Diag(Tok, diag::warn_omp_extra_tokens_at_eol) << getOpenMPDirectiveName(OMPD_threadprivate); SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch); } DeclGroupPtrTy Res = Actions.ActOnOpenMPThreadprivateDirective(Loc, Identifiers); Directive = Actions.ActOnDeclStmt(Res, Loc, Tok.getLocation()); } SkipUntil(tok::annot_pragma_openmp_end); break; case OMPD_parallel: { ConsumeToken(); Actions.StartOpenMPDSABlock(DKind, DirName, Actions.getCurScope()); while (Tok.isNot(tok::annot_pragma_openmp_end)) { OpenMPClauseKind CKind = Tok.isAnnotation() ? OMPC_unknown : getOpenMPClauseKind(PP.getSpelling(Tok)); OMPClause *Clause = ParseOpenMPClause(DKind, CKind, !FirstClauses[CKind].getInt()); FirstClauses[CKind].setInt(true); if (Clause) { FirstClauses[CKind].setPointer(Clause); Clauses.push_back(Clause); } // Skip ',' if any. if (Tok.is(tok::comma)) ConsumeToken(); } // End location of the directive. EndLoc = Tok.getLocation(); // Consume final annot_pragma_openmp_end. ConsumeToken(); StmtResult AssociatedStmt; bool CreateDirective = true; ParseScope OMPDirectiveScope(this, ScopeFlags); { // The body is a block scope like in Lambdas and Blocks. Sema::CompoundScopeRAII CompoundScope(Actions); Actions.ActOnCapturedRegionStart(Loc, getCurScope(), CR_OpenMP, 1); Actions.ActOnStartOfCompoundStmt(); // Parse statement AssociatedStmt = ParseStatement(); Actions.ActOnFinishOfCompoundStmt(); if (!AssociatedStmt.isUsable()) { Actions.ActOnCapturedRegionError(); CreateDirective = false; } else { AssociatedStmt = Actions.ActOnCapturedRegionEnd(AssociatedStmt.take()); CreateDirective = AssociatedStmt.isUsable(); } } if (CreateDirective) Directive = Actions.ActOnOpenMPExecutableDirective(DKind, Clauses, AssociatedStmt.take(), Loc, EndLoc); // Exit scope. Actions.EndOpenMPDSABlock(Directive.get()); OMPDirectiveScope.Exit(); } break; case OMPD_unknown: Diag(Tok, diag::err_omp_unknown_directive); SkipUntil(tok::annot_pragma_openmp_end); break; case OMPD_task: case NUM_OPENMP_DIRECTIVES: Diag(Tok, diag::err_omp_unexpected_directive) << getOpenMPDirectiveName(DKind); SkipUntil(tok::annot_pragma_openmp_end); break; } return Directive; }
bool CoroutineStmtBuilder::makeGroDeclAndReturnStmt() { assert(!IsPromiseDependentType && "cannot make statement while the promise type is dependent"); assert(this->ReturnValue && "ReturnValue must be already formed"); QualType const GroType = this->ReturnValue->getType(); assert(!GroType->isDependentType() && "get_return_object type must no longer be dependent"); QualType const FnRetType = FD.getReturnType(); assert(!FnRetType->isDependentType() && "get_return_object type must no longer be dependent"); if (FnRetType->isVoidType()) { ExprResult Res = S.ActOnFinishFullExpr(this->ReturnValue, Loc); if (Res.isInvalid()) return false; this->ResultDecl = Res.get(); return true; } if (GroType->isVoidType()) { // Trigger a nice error message. InitializedEntity Entity = InitializedEntity::InitializeResult(Loc, FnRetType, false); S.PerformMoveOrCopyInitialization(Entity, nullptr, FnRetType, ReturnValue); noteMemberDeclaredHere(S, ReturnValue, Fn); return false; } auto *GroDecl = VarDecl::Create( S.Context, &FD, FD.getLocation(), FD.getLocation(), &S.PP.getIdentifierTable().get("__coro_gro"), GroType, S.Context.getTrivialTypeSourceInfo(GroType, Loc), SC_None); S.CheckVariableDeclarationType(GroDecl); if (GroDecl->isInvalidDecl()) return false; InitializedEntity Entity = InitializedEntity::InitializeVariable(GroDecl); ExprResult Res = S.PerformMoveOrCopyInitialization(Entity, nullptr, GroType, this->ReturnValue); if (Res.isInvalid()) return false; Res = S.ActOnFinishFullExpr(Res.get()); if (Res.isInvalid()) return false; if (GroType == FnRetType) { GroDecl->setNRVOVariable(true); } S.AddInitializerToDecl(GroDecl, Res.get(), /*DirectInit=*/false); S.FinalizeDeclaration(GroDecl); // Form a declaration statement for the return declaration, so that AST // visitors can more easily find it. StmtResult GroDeclStmt = S.ActOnDeclStmt(S.ConvertDeclToDeclGroup(GroDecl), Loc, Loc); if (GroDeclStmt.isInvalid()) return false; this->ResultDecl = GroDeclStmt.get(); ExprResult declRef = S.BuildDeclRefExpr(GroDecl, GroType, VK_LValue, Loc); if (declRef.isInvalid()) return false; StmtResult ReturnStmt = S.BuildReturnStmt(Loc, declRef.get()); if (ReturnStmt.isInvalid()) { noteMemberDeclaredHere(S, ReturnValue, Fn); return false; } this->ReturnStmt = ReturnStmt.get(); return true; }
/// ParseSAVEStmt - Parse the SAVE statement. /// /// [R543]: /// save-stmt := /// SAVE [ [::] saved-entity-list ] Parser::StmtResult Parser::ParseSAVEStmt() { // Check if this is an assignment. if (IsNextToken(tok::equal)) return StmtResult(); auto Loc = ConsumeToken(); if(Tok.isAtStartOfStatement()) return Actions.ActOnSAVE(Context, Loc, StmtLabel); bool IsSaveStmt = ConsumeIfPresent(tok::coloncolon); SmallVector<Stmt *,8> StmtList; bool ListParsedOk = true; auto IDLoc = Tok.getLocation(); auto II = Tok.getIdentifierInfo(); StmtResult Stmt; if(ConsumeIfPresent(tok::slash)) { IDLoc = Tok.getLocation(); II = Tok.getIdentifierInfo(); if(ExpectAndConsume(tok::identifier)) { if(!ExpectAndConsume(tok::slash)) ListParsedOk = false; Stmt = Actions.ActOnSAVECommonBlock(Context, Loc, IDLoc, II); } else ListParsedOk = false; } else if(ExpectAndConsume(tok::identifier)) { if(!IsSaveStmt && Features.FixedForm && (IsPresent(tok::equal) || IsPresent(tok::l_paren))) return ReparseAmbiguousAssignmentStatement(); Stmt = Actions.ActOnSAVE(Context, Loc, IDLoc, II, nullptr); } else ListParsedOk = false; if(Stmt.isUsable()) StmtList.push_back(Stmt.get()); if(ListParsedOk) { while(ConsumeIfPresent(tok::comma)) { IDLoc = Tok.getLocation(); II = Tok.getIdentifierInfo(); if(ConsumeIfPresent(tok::slash)) { IDLoc = Tok.getLocation(); II = Tok.getIdentifierInfo(); if(!ExpectAndConsume(tok::identifier)) { ListParsedOk = false; break; } if(!ExpectAndConsume(tok::slash)) { ListParsedOk = false; break; } Stmt = Actions.ActOnSAVECommonBlock(Context, Loc, IDLoc, II); } else if(ExpectAndConsume(tok::identifier)) Stmt = Actions.ActOnSAVE(Context, Loc, IDLoc, II, nullptr); else { ListParsedOk = false; break; } if(Stmt.isUsable()) StmtList.push_back(Stmt.get()); } } if(ListParsedOk) ExpectStatementEnd(); else SkipUntilNextStatement(); return Actions.ActOnCompoundStmt(Context, Loc, StmtList, StmtLabel); }