summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorarphaman <arphaman@gmail.com>2013-07-24 14:48:03 +0100
committerarphaman <arphaman@gmail.com>2013-07-24 14:48:03 +0100
commited590604f4cf71a16d71cd18358f307d186de55c (patch)
treee1e3366373aa8f17e29a23b03694e314872b2652
parent23292c947a37c89d024299768f9de4c17211d268 (diff)
downloadflang-ed590604f4cf71a16d71cd18358f307d186de55c.tar.gz
improved construct termination and control flow error recovery
-rw-r--r--include/flang/Basic/DiagnosticSemaKinds.td6
-rw-r--r--include/flang/Sema/Scope.h12
-rw-r--r--include/flang/Sema/Sema.h19
-rw-r--r--lib/Sema/Sema.cpp42
-rw-r--r--lib/Sema/SemaExecStmt.cpp354
-rw-r--r--test/Parser/do.f953
-rw-r--r--test/Parser/if.f954
-rw-r--r--test/Sema/beginEndRecovery.f9536
-rw-r--r--test/Sema/do.f9513
-rw-r--r--test/Sema/dowhile.f956
-rw-r--r--test/Sema/if.f9512
-rw-r--r--test/Sema/subprogram.f955
12 files changed, 283 insertions, 229 deletions
diff --git a/include/flang/Basic/DiagnosticSemaKinds.td b/include/flang/Basic/DiagnosticSemaKinds.td
index f96aece914..dc3f105672 100644
--- a/include/flang/Basic/DiagnosticSemaKinds.td
+++ b/include/flang/Basic/DiagnosticSemaKinds.td
@@ -112,15 +112,15 @@ def warn_unused_stmt_label : Warning<"unused statement label '%0'">,
InGroup<UnusedLabel>, DefaultIgnore;
def err_expected_stmt_label_end_do : Error<
- "expected a statement with a statement label '%0' to mark the end of a do loop">;
+ "expected a do termination statement with a statement label '%0'">;
def err_invalid_do_terminating_stmt : Error<
"invalid terminating statement for a DO loop">;
def err_stmt_not_in_if : Error<
- "'%0' statement must be a part of an if construct">;
+ "use of '%0' outside an if construct">;
def err_end_do_without_do : Error<
- "use of 'END DO' without the do statement">;
+ "use of 'end do' outside a do construct">;
def err_use_implicit_none_stmt : Error<
"use of 'IMPLICIT NONE' after 'IMPLICIT'">;
diff --git a/include/flang/Sema/Scope.h b/include/flang/Sema/Scope.h
index 8036cc4e8a..d04f0bfb71 100644
--- a/include/flang/Sema/Scope.h
+++ b/include/flang/Sema/Scope.h
@@ -56,8 +56,9 @@ public:
Entry(IfStmt *S)
: Statement(S), BeginOffset(0) {
}
- inline bool is(Stmt::StmtClass StmtType) const {
- return Statement->getStmtClass() == StmtType;
+
+ bool hasExpectedDoLabel() const {
+ return ExpectedEndDoLabel != nullptr;
}
};
@@ -75,13 +76,12 @@ public:
return StmtList;
}
- inline const Entry &LastEntered() const {
+ const Entry &LastEntered() const {
return ControlFlowStack.back();
}
- inline bool HasEntered() const {
+ bool HasEntered() const {
return ControlFlowStack.size() != 0;
}
- bool HasEntered(Stmt::StmtClass StmtType) const;
void Append(Stmt *S);
private:
@@ -191,7 +191,7 @@ public:
bool ApplyNone();
/// \brief returns true if IMPLICIT NONE was used in this scope.
- inline bool isNoneInThisScope() const {
+ bool isNoneInThisScope() const {
return None;
}
diff --git a/include/flang/Sema/Sema.h b/include/flang/Sema/Sema.h
index f09e8d0cd6..dfe25865ed 100644
--- a/include/flang/Sema/Sema.h
+++ b/include/flang/Sema/Sema.h
@@ -600,6 +600,25 @@ public:
SourceLocation Loc,
bool ReportUnterminatedLabeledDo = true);
+ /// Leaves the last block construct, and performs any clean up
+ /// that might be needed.
+ void LeaveLastBlock();
+
+ /// Leaves block constructs until a do construct is reached.
+ /// NB: the do statements with a termination label such as DO 100 I = ..
+ /// are popped.
+ Stmt *LeaveBlocksUntilDo(SourceLocation Loc);
+
+ /// Returns true if the current statement is inside a do construct which
+ /// is terminated by the given statement label.
+ bool IsInLabeledDo(const Expr *StmtLabel);
+
+ /// Leaves block constructs until a label termination do construct is reached.
+ DoStmt *LeaveBlocksUntilLabeledDo(SourceLocation Loc, const Expr *StmtLabel);
+
+ /// Leaves block constructs until an if construct is reached.
+ IfStmt *LeaveBlocksUntilIf(SourceLocation Loc);
+
};
} // end flang namespace
diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp
index 1bf7b7bd86..7275cd9793 100644
--- a/lib/Sema/Sema.cpp
+++ b/lib/Sema/Sema.cpp
@@ -76,39 +76,6 @@ void Sema::PushExecutableProgramUnit(ExecutableProgramUnitScope &Scope) {
CurExecutableStmts = &Scope.Body;
}
-// Unterminated labeled do statement
-static void ReportUnterminatedLabeledDoStmt(DiagnosticsEngine &Diags,
- const BlockStmtBuilder::Entry &S,
- SourceLocation Loc) {
- std::string Str;
- llvm::raw_string_ostream Stream(Str);
- S.ExpectedEndDoLabel->dump(Stream);
- Diags.Report(Loc, diag::err_expected_stmt_label_end_do) << Stream.str();
-}
-
-// Unterminated if/do statement
-void Sema::ReportUnterminatedStmt(const BlockStmtBuilder::Entry &S,
- SourceLocation Loc,
- bool ReportUnterminatedLabeledDo) {
- const char * Keyword;
- switch(S.Statement->getStmtClass()) {
- case Stmt::IfStmtClass: Keyword = "END IF"; break;
- case Stmt::DoWhileStmtClass:
- case Stmt::DoStmtClass: {
- if(S.ExpectedEndDoLabel) {
- if(ReportUnterminatedLabeledDo)
- ReportUnterminatedLabeledDoStmt(Diags, S, Loc);
- return;
- }
- else Keyword = "END DO";
- break;
- }
- default:
- llvm_unreachable("Invalid stmt");
- }
- Diags.Report(Loc, diag::err_expected_kw) << Keyword;
-}
-
void Sema::PopExecutableProgramUnit(SourceLocation Loc) {
// Fix the forward statement label references
@@ -162,7 +129,7 @@ void BlockStmtBuilder::Enter(Entry S) {
}
Stmt *BlockStmtBuilder::CreateBody(ASTContext &C,
- const Entry &Last) {
+ const Entry &Last) {
auto Ref = ArrayRef<Stmt*>(StmtList);
return BlockStmt::Create(C, Last.Statement->getLocation(),
ArrayRef<Stmt*>(Ref.begin() + Last.BeginOffset,
@@ -198,13 +165,6 @@ Stmt *BlockStmtBuilder::LeaveOuterBody(ASTContext &C, SourceLocation Loc) {
return BlockStmt::Create(C, Loc, StmtList);
}
-bool BlockStmtBuilder::HasEntered(Stmt::StmtClass StmtType) const {
- for(auto I : ControlFlowStack) {
- if(I.is(StmtType)) return true;
- }
- return false;
-}
-
void BlockStmtBuilder::Append(Stmt *S) {
assert(S);
StmtList.push_back(S);
diff --git a/lib/Sema/SemaExecStmt.cpp b/lib/Sema/SemaExecStmt.cpp
index d138d448d0..708dc00097 100644
--- a/lib/Sema/SemaExecStmt.cpp
+++ b/lib/Sema/SemaExecStmt.cpp
@@ -7,8 +7,8 @@
//
//===----------------------------------------------------------------------===//
//
-// This file implements the checking and AST construction for the DATA
-// statement.
+// This file implements the checking and AST construction for the executable
+// statements.
//
//===----------------------------------------------------------------------===//
@@ -16,6 +16,7 @@
#include "flang/Sema/DeclSpec.h"
#include "flang/Sema/SemaDiagnostic.h"
#include "flang/Sema/SemaInternal.h"
+#include "flang/Parse/ParseDiagnostic.h"
#include "flang/AST/ASTContext.h"
#include "flang/AST/Decl.h"
#include "flang/AST/Stmt.h"
@@ -103,6 +104,10 @@ StmtResult Sema::ActOnGotoStmt(ASTContext &C, SourceLocation Loc,
return Result;
}
+// =========================================================================
+// Block statements entry
+// =========================================================================
+
StmtResult Sema::ActOnIfStmt(ASTContext &C, SourceLocation Loc,
ExprResult Condition, Expr *StmtLabel) {
if(Condition.isUsable())
@@ -116,98 +121,6 @@ StmtResult Sema::ActOnIfStmt(ASTContext &C, SourceLocation Loc,
return Result;
}
-StmtResult Sema::ActOnElseIfStmt(ASTContext &C, SourceLocation Loc,
- ExprResult Condition, Expr *StmtLabel) {
- if(!getCurrentBody()->HasEntered() ||
- !getCurrentBody()->LastEntered().is(Stmt::IfStmtClass)) {
- if(getCurrentBody()->HasEntered(Stmt::IfStmtClass))
- ReportUnterminatedStmt( getCurrentBody()->LastEntered(), Loc);
- else
- Diags.Report(Loc, diag::err_stmt_not_in_if) << "ELSE IF";
- return StmtError();
- }
-
- // typecheck
- if(Condition.isUsable())
- StmtRequiresLogicalExpression(Loc, Condition.get());
-
- auto Result = IfStmt::Create(C, Loc, Condition.get(), StmtLabel);
- auto ParentIf = cast<IfStmt>(getCurrentBody()->LastEntered().Statement);
- getCurrentBody()->Leave(C);
- if(Condition.isUsable())
- ParentIf->setElseStmt(Result);
- if(StmtLabel) DeclareStatementLabel(StmtLabel, Result);
- getCurrentBody()->Enter(Result);
- return Result;
-}
-
-StmtResult Sema::ActOnElseStmt(ASTContext &C, SourceLocation Loc, Expr *StmtLabel) {
- if(!getCurrentBody()->HasEntered() ||
- !getCurrentBody()->LastEntered().is(Stmt::IfStmtClass)) {
- if(getCurrentBody()->HasEntered(Stmt::IfStmtClass))
- ReportUnterminatedStmt(getCurrentBody()->LastEntered(), Loc);
- else
- Diags.Report(Loc, diag::err_stmt_not_in_if) << "ELSE";
- return StmtError();
- }
- auto Result = ConstructPartStmt::Create(C, ConstructPartStmt::ElseStmtClass, Loc, nullptr, StmtLabel);
- getCurrentBody()->Append(Result);
- getCurrentBody()->LeaveIfThen(C);
- if(StmtLabel) DeclareStatementLabel(StmtLabel, Result);
- return Result;
-}
-
-StmtResult Sema::ActOnEndIfStmt(ASTContext &C, SourceLocation Loc, Expr *StmtLabel) {
- // Report begin .. end mismatch
- if(!getCurrentBody()->HasEntered() ||
- !getCurrentBody()->LastEntered().is(Stmt::IfStmtClass)) {
- if(getCurrentBody()->HasEntered(Stmt::IfStmtClass))
- ReportUnterminatedStmt(getCurrentBody()->LastEntered(), Loc);
- else
- Diags.Report(Loc, diag::err_stmt_not_in_if) << "END IF";
- return StmtError();
- }
-
- auto Result = ConstructPartStmt::Create(C, ConstructPartStmt::EndIfStmtClass, Loc, nullptr, StmtLabel);
- getCurrentBody()->Append(Result);
- getCurrentBody()->Leave(C);
- if(StmtLabel) DeclareStatementLabel(StmtLabel, Result);
- return Result;
-}
-
-/// The terminal statement of a DO-loop must not be an unconditional GO TO,
-/// assigned GO TO, arithmetic IF, block IF, ELSE IF, ELSE, END IF, RETURN, STOP, END, or DO statement.
-/// If the terminal statement of a DO-loop is a logical IF statement,
-/// it may contain any executable statement except a DO,
-/// block IF, ELSE IF, ELSE, END IF, END, or another logical IF statement.
-///
-/// FIXME: TODO full
-static bool IsValidDoLogicalIfThenStatement(const Stmt *S) {
- switch(S->getStmtClass()) {
- case Stmt::DoStmtClass: case Stmt::IfStmtClass: case Stmt::DoWhileStmtClass:
- case Stmt::ConstructPartStmtClass:
- return false;
- default:
- return true;
- }
-}
-
-bool Sema::IsValidDoTerminatingStatement(const Stmt *S) {
- switch(S->getStmtClass()) {
- case Stmt::GotoStmtClass: case Stmt::AssignedGotoStmtClass:
- case Stmt::StopStmtClass: case Stmt::DoStmtClass:
- case Stmt::DoWhileStmtClass:
- case Stmt::ConstructPartStmtClass:
- return false;
- case Stmt::IfStmtClass: {
- auto NextStmt = cast<IfStmt>(S)->getThenStmt();
- return NextStmt && IsValidDoLogicalIfThenStatement(NextStmt);
- }
- default:
- return true;
- }
-}
-
void StmtLabelResolver::VisitDoStmt(DoStmt *S) {
S->setTerminatingStmt(StmtLabelReference(StmtLabelDecl));
}
@@ -271,44 +184,6 @@ StmtResult Sema::ActOnDoStmt(ASTContext &C, SourceLocation Loc, SourceLocation E
return Result;
}
-/// FIXME: Fortran 90+: make multiple do end at one label obsolete
-void Sema::CheckStatementLabelEndDo(Expr *StmtLabel, Stmt *S) {
- if(!getCurrentBody()->HasEntered())
- return;
-
- auto I = getCurrentBody()->ControlFlowStack.size();
- size_t LastUnterminated = 0;
- do {
- I--;
- if(!isa<DoStmt>(getCurrentBody()->ControlFlowStack[I].Statement)) {
- if(!LastUnterminated) LastUnterminated = I;
- } else {
- auto ParentDo = cast<DoStmt>(getCurrentBody()->ControlFlowStack[I].Statement);
- auto ParentDoExpectedLabel = getCurrentBody()->ControlFlowStack[I].ExpectedEndDoLabel;
- if(!ParentDoExpectedLabel) {
- if(!LastUnterminated) LastUnterminated = I;
- } else {
- if(getCurrentStmtLabelScope()->IsSame(ParentDoExpectedLabel, StmtLabel)) {
- // END DO
- getCurrentStmtLabelScope()->RemoveForwardReference(ParentDo);
- if(!IsValidDoTerminatingStatement(S)) {
- Diags.Report(S->getLocation(),
- diag::err_invalid_do_terminating_stmt);
- }
- ParentDo->setTerminatingStmt(StmtLabelReference(S));
- if(!LastUnterminated) {
- RemoveLoopVar(ParentDo->getDoVar());
- getCurrentBody()->Leave(Context);
- }
- else
- ReportUnterminatedStmt(getCurrentBody()->ControlFlowStack[LastUnterminated],
- S->getLocation());
- } else if(!LastUnterminated) LastUnterminated = I;
- }
- }
- } while(I>0);
-}
-
StmtResult Sema::ActOnDoWhileStmt(ASTContext &C, SourceLocation Loc, ExprResult Condition,
Expr *StmtLabel) {
if(Condition.isUsable())
@@ -321,43 +196,212 @@ StmtResult Sema::ActOnDoWhileStmt(ASTContext &C, SourceLocation Loc, ExprResult
return Result;
}
-StmtResult Sema::ActOnEndDoStmt(ASTContext &C, SourceLocation Loc, Expr *StmtLabel) {
- // Report begin .. end mismatch
- if(!getCurrentBody()->HasEntered() ||
- (!isa<DoStmt>(getCurrentBody()->LastEntered().Statement) &&
- !isa<DoWhileStmt>(getCurrentBody()->LastEntered().Statement))) {
- bool HasMatchingDo = false;
- for(auto I : getCurrentBody()->ControlFlowStack) {
- if(isa<DoWhileStmt>(I.Statement) ||
- (isa<DoStmt>(I.Statement) && !I.ExpectedEndDoLabel)) {
- HasMatchingDo = true;
- break;
+
+// =============================================================
+// Block statements termination and control flow
+// =============================================================
+
+void Sema::ReportUnterminatedStmt(const BlockStmtBuilder::Entry &S,
+ SourceLocation Loc,
+ bool ReportUnterminatedLabeledDo) {
+ const char *Keyword;
+ const char *BeginKeyword;
+ switch(S.Statement->getStmtClass()) {
+ case Stmt::IfStmtClass:
+ Keyword = "end if";
+ BeginKeyword = "if";
+ break;
+ case Stmt::DoWhileStmtClass:
+ case Stmt::DoStmtClass: {
+ if(S.ExpectedEndDoLabel) {
+ if(ReportUnterminatedLabeledDo) {
+ std::string Str;
+ llvm::raw_string_ostream Stream(Str);
+ S.ExpectedEndDoLabel->dump(Stream);
+ Diags.Report(Loc, diag::err_expected_stmt_label_end_do) << Stream.str();
+ Diags.Report(S.Statement->getLocation(), diag::note_matching) << "do";
}
+ return;
}
- if(!HasMatchingDo)
- Diags.Report(Loc, diag::err_end_do_without_do);
- else ReportUnterminatedStmt(getCurrentBody()->LastEntered(), Loc);
- return StmtError();
+ Keyword = "end do";
+ BeginKeyword = "do";
+ break;
+ }
+ default:
+ llvm_unreachable("Invalid stmt");
+ }
+ Diags.Report(Loc, diag::err_expected_kw) << Keyword;
+ Diags.Report(S.Statement->getLocation(), diag::note_matching) << BeginKeyword;
+}
+
+void Sema::LeaveLastBlock() {
+ auto Last = getCurrentBody()->LastEntered().Statement;
+ if(auto Do = dyn_cast<DoStmt>(Last)) {
+ RemoveLoopVar(Do->getDoVar());
}
+ getCurrentBody()->Leave(Context);
+}
- auto Last = getCurrentBody()->LastEntered();
+IfStmt *Sema::LeaveBlocksUntilIf(SourceLocation Loc) {
+ auto Stack = getCurrentBody()->ControlFlowStack;
+ for(size_t I = Stack.size(); I != 0;) {
+ --I;
+ if(auto If = dyn_cast<IfStmt>(Stack[I].Statement))
+ return If;
+ ReportUnterminatedStmt(Stack[I], Loc);
+ LeaveLastBlock();
+ }
+ return nullptr;
+}
- if(auto Do = dyn_cast<DoStmt>(Last.Statement)) {
- // If last loop was a DO with terminating label, we expect it to finish before this loop
- if(getCurrentBody()->LastEntered().ExpectedEndDoLabel) {
- //FIXME: ReportUnterminatedLabeledDoStmt(getCurrentBody()->LastEntered(), Loc);
- return StmtError();
+Stmt *Sema::LeaveBlocksUntilDo(SourceLocation Loc) {
+ auto Stack = getCurrentBody()->ControlFlowStack;
+ for(size_t I = Stack.size(); I != 0;) {
+ --I;
+ auto S = Stack[I].Statement;
+ if(isa<DoWhileStmt>(S) ||
+ isa<DoStmt>(S) && !Stack[I].hasExpectedDoLabel())
+ return S;
+ ReportUnterminatedStmt(Stack[I], Loc);
+ LeaveLastBlock();
+ }
+ return nullptr;
+}
+
+/// The terminal statement of a DO-loop must not be an unconditional GO TO,
+/// assigned GO TO, arithmetic IF, block IF, ELSE IF, ELSE, END IF, RETURN, STOP, END, or DO statement.
+/// If the terminal statement of a DO-loop is a logical IF statement,
+/// it may contain any executable statement except a DO,
+/// block IF, ELSE IF, ELSE, END IF, END, or another logical IF statement.
+///
+/// FIXME: TODO full
+static bool IsValidDoLogicalIfThenStatement(const Stmt *S) {
+ switch(S->getStmtClass()) {
+ case Stmt::DoStmtClass: case Stmt::IfStmtClass: case Stmt::DoWhileStmtClass:
+ case Stmt::ConstructPartStmtClass:
+ return false;
+ default:
+ return true;
+ }
+}
+
+bool Sema::IsValidDoTerminatingStatement(const Stmt *S) {
+ switch(S->getStmtClass()) {
+ case Stmt::GotoStmtClass: case Stmt::AssignedGotoStmtClass:
+ case Stmt::StopStmtClass: case Stmt::DoStmtClass:
+ case Stmt::DoWhileStmtClass:
+ case Stmt::ConstructPartStmtClass:
+ return false;
+ case Stmt::IfStmtClass: {
+ auto NextStmt = cast<IfStmt>(S)->getThenStmt();
+ return NextStmt && IsValidDoLogicalIfThenStatement(NextStmt);
+ }
+ default:
+ return true;
+ }
+}
+
+bool Sema::IsInLabeledDo(const Expr *StmtLabel) {
+ auto Stack = getCurrentBody()->ControlFlowStack;
+ for(size_t I = Stack.size(); I != 0;) {
+ --I;
+ if(isa<DoStmt>(Stack[I].Statement)) {
+ if(Stack[I].hasExpectedDoLabel()) {
+ if(getCurrentStmtLabelScope()->IsSame(Stack[I].ExpectedEndDoLabel, StmtLabel))
+ return true;
+ }
}
- RemoveLoopVar(Do->getDoVar());
}
+ return false;
+}
+
+DoStmt *Sema::LeaveBlocksUntilLabeledDo(SourceLocation Loc, const Expr *StmtLabel) {
+ auto Stack = getCurrentBody()->ControlFlowStack;
+ for(size_t I = Stack.size(); I != 0;) {
+ --I;
+ if(auto Do = dyn_cast<DoStmt>(Stack[I].Statement)) {
+ if(Stack[I].hasExpectedDoLabel()) {
+ if(getCurrentStmtLabelScope()->IsSame(Stack[I].ExpectedEndDoLabel, StmtLabel))
+ return Do;
+ }
+ }
+ ReportUnterminatedStmt(Stack[I], Loc);
+ LeaveLastBlock();
+ }
+ return nullptr;
+}
+
+StmtResult Sema::ActOnElseIfStmt(ASTContext &C, SourceLocation Loc,
+ ExprResult Condition, Expr *StmtLabel) {
+ auto IfBegin = LeaveBlocksUntilIf(Loc);
+ if(!IfBegin)
+ Diags.Report(Loc, diag::err_stmt_not_in_if) << "else if";
+
+ // typecheck
+ if(Condition.isUsable())
+ StmtRequiresLogicalExpression(Loc, Condition.get());
+
+ auto Result = IfStmt::Create(C, Loc, Condition.get(), StmtLabel);
+ if(IfBegin) {
+ LeaveLastBlock();
+ if(Condition.isUsable())
+ IfBegin->setElseStmt(Result);
+ }
+ if(StmtLabel) DeclareStatementLabel(StmtLabel, Result);
+ getCurrentBody()->Enter(Result);
+ return Result;
+}
+
+StmtResult Sema::ActOnElseStmt(ASTContext &C, SourceLocation Loc, Expr *StmtLabel) {
+ auto IfBegin = LeaveBlocksUntilIf(Loc);
+ if(!IfBegin)
+ Diags.Report(Loc, diag::err_stmt_not_in_if) << "else";
+
+ auto Result = ConstructPartStmt::Create(C, ConstructPartStmt::ElseStmtClass, Loc, nullptr, StmtLabel);
+ getCurrentBody()->Append(Result);
+ if(IfBegin) getCurrentBody()->LeaveIfThen(C);
+ if(StmtLabel) DeclareStatementLabel(StmtLabel, Result);
+ return Result;
+}
+
+StmtResult Sema::ActOnEndIfStmt(ASTContext &C, SourceLocation Loc, Expr *StmtLabel) {
+ auto IfBegin = LeaveBlocksUntilIf(Loc);
+ if(!IfBegin)
+ Diags.Report(Loc, diag::err_stmt_not_in_if) << "end if";
+
+ auto Result = ConstructPartStmt::Create(C, ConstructPartStmt::EndIfStmtClass, Loc, nullptr, StmtLabel);
+ getCurrentBody()->Append(Result);
+ if(IfBegin) LeaveLastBlock();
+ if(StmtLabel) DeclareStatementLabel(StmtLabel, Result);
+ return Result;
+}
+
+StmtResult Sema::ActOnEndDoStmt(ASTContext &C, SourceLocation Loc, Expr *StmtLabel) {
+ auto DoBegin = LeaveBlocksUntilDo(Loc);
+ if(!DoBegin)
+ Diags.Report(Loc, diag::err_end_do_without_do);
auto Result = ConstructPartStmt::Create(C, ConstructPartStmt::EndDoStmtClass, Loc, nullptr, StmtLabel);
getCurrentBody()->Append(Result);
- getCurrentBody()->Leave(C);
+ if(DoBegin) LeaveLastBlock();
if(StmtLabel) DeclareStatementLabel(StmtLabel, Result);
return Result;
}
+/// FIXME: Fortran 90+: make multiple do end at one label obsolete
+void Sema::CheckStatementLabelEndDo(Expr *StmtLabel, Stmt *S) {
+ if(!getCurrentBody()->HasEntered()) return;
+ if(!IsInLabeledDo(StmtLabel)) return;
+ auto DoBegin = LeaveBlocksUntilLabeledDo(S->getLocation(), StmtLabel);
+
+ getCurrentStmtLabelScope()->RemoveForwardReference(DoBegin);
+ if(!IsValidDoTerminatingStatement(S))
+ Diags.Report(S->getLocation(), diag::err_invalid_do_terminating_stmt);
+ DoBegin->setTerminatingStmt(StmtLabelReference(S));
+ LeaveLastBlock();
+}
+
+
StmtResult Sema::ActOnContinueStmt(ASTContext &C, SourceLocation Loc, Expr *StmtLabel) {
auto Result = ContinueStmt::Create(C, Loc, StmtLabel);
getCurrentBody()->Append(Result);
diff --git a/test/Parser/do.f95 b/test/Parser/do.f95
index 2f3d69a94a..d732f491a7 100644
--- a/test/Parser/do.f95
+++ b/test/Parser/do.f95
@@ -50,7 +50,6 @@ PROGRAM dotest
ENDDO
DO x I = 1,2 ! expected-error {{expected '='}}
- ! END DO
+ END DO
- DO I = 1,5 ! expected-error@+1 {{expected 'END DO'}}
END PROGRAM
diff --git a/test/Parser/if.f95 b/test/Parser/if.f95
index 2c3397bc13..7657c53155 100644
--- a/test/Parser/if.f95
+++ b/test/Parser/if.f95
@@ -56,6 +56,6 @@ PROGRAM iftest
ENDIF
END IF
- IF(1 == 2) THEN
- C = "NO" ! expected-error@+1 {{expected 'END IF'}}
+ IF(1 == 2) THEN ! expected-note {{to match this 'if'}}
+ C = "NO" ! expected-error@+1 {{expected 'end if'}}
END PROGRAM iftest
diff --git a/test/Sema/beginEndRecovery.f95 b/test/Sema/beginEndRecovery.f95
new file mode 100644
index 0000000000..4defe9b5db
--- /dev/null
+++ b/test/Sema/beginEndRecovery.f95
@@ -0,0 +1,36 @@
+! RUN: %flang -fsyntax-only -verify < %s
+
+program recovery
+
+ do i = 1, 10
+ if (i == 0) then ! expected-note {{to match this 'if'}}
+ end do ! expected-error {{expected 'end if'}}
+
+ do while(.true.) ! expected-error@+3 {{expected a do termination statement with a statement label '100'}}
+ do 100 i = 1, 10 ! expected-note {{to match this 'do'}}
+ if (i == 0) then ! expected-note {{to match this 'if'}}
+ end do ! expected-error {{expected 'end if'}}
+
+ i = 0
+
+ if(i == 1) then
+ do i = 1, 10 ! expected-note {{to match this 'do'}}
+ end if ! expected-error {{expected 'end do'}}
+
+ if(i == 1) then
+ do i = 1, 10 ! expected-note {{to match this 'do'}}
+ else ! expected-error {{expected 'end do'}}
+ end if
+
+ if(i == 1) then
+ do 100 i = 1,10 ! expected-note {{to match this 'do'}}
+ else if(i == 3) then ! expected-error {{expected a do termination statement with a statement label '100'}}
+ end if
+
+ do 200 i = 1,10
+ do j = 1,10 ! expected-note {{to match this 'do'}}
+300 print *,i
+200 continue ! expected-error {{expected 'end do'}}
+
+100 continue
+end
diff --git a/test/Sema/do.f95 b/test/Sema/do.f95
index ebe5f50341..ac6accbdbf 100644
--- a/test/Sema/do.f95
+++ b/test/Sema/do.f95
@@ -12,20 +12,19 @@ PROGRAM dotest
R = I * R
10 CONTINUE ! expected-note {{previous definition is here}}
- END DO ! expected-error {{use of 'END DO' without the do statement}}
+ END DO ! expected-error {{use of 'end do' outside a do construct}}
DO 10 I = 1, 5 ! expected-error {{the statement label '10' must be declared after the 'DO' statement}}
20 R = R * R
DO 25 II = 1, 10
- IF(.true.) THEN
-25 CONTINUE ! expected-error {{expected 'END IF'}}
- END IF
+ IF(.true.) THEN ! expected-note {{to match this 'if'}}
+25 CONTINUE ! expected-error {{expected 'end if'}}
DO 666 I = 1, 10,2 ! expected-error {{use of undeclared statement label '666'}}
- R = I * R
-
- END DO ! expected-error {{expected a statement with a statement label '666' to mark the end of a do loop}}
+ R = I * R ! expected-note@-1 {{to match this 'do'}}
+ END DO ! expected-error {{expected a do termination statement with a statement label '666'}}
+ CONTINUE ! expected-error@-1 {{use of 'end do' outside a do construct}}
DO 30 C = 1, 3 ! expected-error {{statement requires an integer variable ('complex' invalid)}}
30 CONTINUE
diff --git a/test/Sema/dowhile.f95 b/test/Sema/dowhile.f95
index 938a3c2fee..6583c6de3b 100644
--- a/test/Sema/dowhile.f95
+++ b/test/Sema/dowhile.f95
@@ -8,8 +8,8 @@ PROGRAM dowhiletest
END DO
IF(.true.) THEN
- DO WHILE(.false.)
+ DO WHILE(.false.) ! expected-note {{to match this 'do'}}
- END IF ! expected-error {{expected 'END DO'}}
+ END IF ! expected-error {{expected 'end do'}}
-END ! expected-error {{expected 'END DO'}}
+END
diff --git a/test/Sema/if.f95 b/test/Sema/if.f95
index e5b8e40b5a..679711c6bc 100644
--- a/test/Sema/if.f95
+++ b/test/Sema/if.f95
@@ -10,9 +10,9 @@ PROGRAM iftest
I = 2
END IF
- ELSE ! expected-error {{'ELSE' statement must be a part of an if construct}}
- ELSE IF(2 == 2) THEN ! expected-error {{'ELSE IF' statement must be a part of an if construct}}
- END IF ! expected-error {{'END IF' statement must be a part of an if construct}}
+ ELSE ! expected-error {{use of 'else' outside an if construct}}
+ ELSE IF(2 == 2) THEN ! expected-error {{use of 'else if' outside an if construct}}
+ END IF ! FIXME: report as out of place end if?
IF(2) THEN ! expected-error {{statement requires an expression of logical type ('integer' invalid)}}
I = 3
@@ -28,8 +28,4 @@ PROGRAM iftest
I = 3
END IF
- IF(.true.) THEN
- DO I = 1, 10
- END IF ! expected-error {{expected 'END DO'}}
-
-END PROGRAM ! expected-error {{expected 'END DO'}}
+END PROGRAM
diff --git a/test/Sema/subprogram.f95 b/test/Sema/subprogram.f95
index fd21665569..88d51a68ab 100644
--- a/test/Sema/subprogram.f95
+++ b/test/Sema/subprogram.f95
@@ -22,8 +22,9 @@ END
SUBROUTINE SUBB ! expected-note {{previous definition is here}}
INTEGER SUBB ! expected-error {{redefinition of 'subb'}}
REAL FUNC2
- IF(SUBB .EQ. 0) FUNC2 = 0 ! expected-error {{expected a variable}}
-END ! expected-error {{expected 'END IF'}}
+ LOGICAL L
+ L = SUBB .EQ. 0 ! expected-error {{expected a variable}}
+END
FUNCTION FUNC3()
INTEGER FUNC3