[clang] [clang-repl] Names declared in if conditions and for-init statements are local to the inner context (C++ 3.3.2p4) (PR #84150)
Stefan Gränitz via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 6 04:05:33 PST 2024
https://github.com/weliveindetail updated https://github.com/llvm/llvm-project/pull/84150
>From 27ff0ddc0d736f4959abe61c9fd43b9c48ffb6a6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Wed, 28 Feb 2024 15:45:53 +0100
Subject: [PATCH 1/7] Add test cases for variable definitions inside for-init
and condition statements
Interpreter/execute-stmts.cpp fails with:
```
i = 2
i = 5
i = 5
i = 5
j = 4
error: Parsing failed.
error: Parsing failed.
error: 'expected-error' diagnostics seen but not expected:
Line 1: redefinition of 'i'
Line 1: redefinition of 'i'
error: 'expected-note' diagnostics seen but not expected:
Line 1: previous definition is here
Line 1: previous definition is here
```
---
clang/test/Interpreter/execute-stmts.cpp | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/clang/test/Interpreter/execute-stmts.cpp b/clang/test/Interpreter/execute-stmts.cpp
index 2d4c17e0c91e66..34a2a35c0027d8 100644
--- a/clang/test/Interpreter/execute-stmts.cpp
+++ b/clang/test/Interpreter/execute-stmts.cpp
@@ -9,6 +9,8 @@
//CODEGEN-CHECK-COUNT-2: define internal void @__stmts__
//CODEGEN-CHECK-NOT: define internal void @__stmts__
+// New tests fail right now
+// XFAIL: *
extern "C" int printf(const char*,...);
@@ -41,3 +43,9 @@ for (; i > 4; --i) { printf("i = %d\n", i); };
int j = i; printf("j = %d\n", j);
// CHECK-NEXT: j = 4
+
+if (int i = j) printf("i = %d\n", i);
+// CHECK-NEXT: i = 4
+
+for (int i = j; i > 3; --i) printf("i = %d\n", i);
+// CHECK-NEXT: i = 4
>From b97605ae98b10ef9cbb1544f997c6a7fed917bf3 Mon Sep 17 00:00:00 2001
From: Vassil Vassilev <v.g.vassilev at gmail.com>
Date: Mon, 4 Mar 2024 17:51:43 +0000
Subject: [PATCH 2/7] Try to fix llvm/llvm-project#83028
---
clang/include/clang/AST/Decl.h | 16 ++++++++++------
clang/include/clang/AST/DeclBase.h | 1 +
clang/include/clang/Basic/DeclNodes.td | 2 +-
clang/include/clang/Sema/Sema.h | 3 ++-
clang/lib/AST/Decl.cpp | 11 ++++++++---
clang/lib/AST/DeclBase.cpp | 1 +
clang/lib/Parse/ParseDecl.cpp | 24 ++++++++++++++++--------
clang/lib/Sema/SemaDecl.cpp | 13 ++++++++++---
clang/test/Interpreter/execute-stmts.cpp | 10 ++++++++++
9 files changed, 59 insertions(+), 22 deletions(-)
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 61117cc5ce71f9..bca9d42c7e0586 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -4419,7 +4419,7 @@ class FileScopeAsmDecl : public Decl {
///
/// \note This is used in libInterpreter, clang -cc1 -fincremental-extensions
/// and in tools such as clang-repl.
-class TopLevelStmtDecl : public Decl {
+class TopLevelStmtDecl : public Decl, public DeclContext {
friend class ASTDeclReader;
friend class ASTDeclWriter;
@@ -4427,7 +4427,7 @@ class TopLevelStmtDecl : public Decl {
bool IsSemiMissing = false;
TopLevelStmtDecl(DeclContext *DC, SourceLocation L, Stmt *S)
- : Decl(TopLevelStmt, DC, L), Statement(S) {}
+ : Decl(TopLevelStmt, DC, L), DeclContext(TopLevelStmt), Statement(S) {}
virtual void anchor();
@@ -4438,15 +4438,19 @@ class TopLevelStmtDecl : public Decl {
SourceRange getSourceRange() const override LLVM_READONLY;
Stmt *getStmt() { return Statement; }
const Stmt *getStmt() const { return Statement; }
- void setStmt(Stmt *S) {
- assert(IsSemiMissing && "Operation supported for printing values only!");
- Statement = S;
- }
+ void setStmt(Stmt *S);
bool isSemiMissing() const { return IsSemiMissing; }
void setSemiMissing(bool Missing = true) { IsSemiMissing = Missing; }
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) { return K == TopLevelStmt; }
+
+ static DeclContext *castToDeclContext(const TopLevelStmtDecl *D) {
+ return static_cast<DeclContext *>(const_cast<TopLevelStmtDecl*>(D));
+ }
+ static TopLevelStmtDecl *castFromDeclContext(const DeclContext *DC) {
+ return static_cast<TopLevelStmtDecl *>(const_cast<DeclContext*>(DC));
+ }
};
/// Represents a block literal declaration, which is like an
diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h
index 9a4736019d1b1b..76810a86a78a46 100644
--- a/clang/include/clang/AST/DeclBase.h
+++ b/clang/include/clang/AST/DeclBase.h
@@ -2120,6 +2120,7 @@ class DeclContext {
case Decl::Block:
case Decl::Captured:
case Decl::ObjCMethod:
+ case Decl::TopLevelStmt:
return true;
default:
return getDeclKind() >= Decl::firstFunction &&
diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td
index 8b1f415dd5fe2c..48396e85c5adac 100644
--- a/clang/include/clang/Basic/DeclNodes.td
+++ b/clang/include/clang/Basic/DeclNodes.td
@@ -95,7 +95,7 @@ def LinkageSpec : DeclNode<Decl>, DeclContext;
def Export : DeclNode<Decl>, DeclContext;
def ObjCPropertyImpl : DeclNode<Decl>;
def FileScopeAsm : DeclNode<Decl>;
-def TopLevelStmt : DeclNode<Decl>;
+def TopLevelStmt : DeclNode<Decl>, DeclContext;
def AccessSpec : DeclNode<Decl>;
def Friend : DeclNode<Decl>;
def FriendTemplate : DeclNode<Decl>;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index f3d3a57104ee07..a79f3c0786ce47 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3263,7 +3263,8 @@ class Sema final {
Decl *ActOnFileScopeAsmDecl(Expr *expr, SourceLocation AsmLoc,
SourceLocation RParenLoc);
- Decl *ActOnTopLevelStmtDecl(Stmt *Statement);
+ TopLevelStmtDecl *ActOnStartTopLevelStmtDecl(Scope *S);
+ void ActOnFinishTopLevelStmtDecl(TopLevelStmtDecl* D, Stmt *Statement);
void ActOnPopScope(SourceLocation Loc, Scope *S);
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 59c039f1f8daeb..d681791d3920c3 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -5552,14 +5552,13 @@ FileScopeAsmDecl *FileScopeAsmDecl::CreateDeserialized(ASTContext &C,
void TopLevelStmtDecl::anchor() {}
TopLevelStmtDecl *TopLevelStmtDecl::Create(ASTContext &C, Stmt *Statement) {
- assert(Statement);
assert(C.getLangOpts().IncrementalExtensions &&
"Must be used only in incremental mode");
- SourceLocation BeginLoc = Statement->getBeginLoc();
+ SourceLocation Loc = Statement ? Statement->getBeginLoc() : SourceLocation();
DeclContext *DC = C.getTranslationUnitDecl();
- return new (C, DC) TopLevelStmtDecl(DC, BeginLoc, Statement);
+ return new (C, DC) TopLevelStmtDecl(DC, Loc, Statement);
}
TopLevelStmtDecl *TopLevelStmtDecl::CreateDeserialized(ASTContext &C,
@@ -5572,6 +5571,12 @@ SourceRange TopLevelStmtDecl::getSourceRange() const {
return SourceRange(getLocation(), Statement->getEndLoc());
}
+void TopLevelStmtDecl::setStmt(Stmt *S) {
+ assert(S);
+ Statement = S;
+ setLocation(Statement->getBeginLoc());
+}
+
void EmptyDecl::anchor() {}
EmptyDecl *EmptyDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L) {
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index 10fe8bb97ce660..fcedb3cfd176a0 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -1352,6 +1352,7 @@ DeclContext *DeclContext::getPrimaryContext() {
case Decl::ExternCContext:
case Decl::LinkageSpec:
case Decl::Export:
+ case Decl::TopLevelStmt:
case Decl::Block:
case Decl::Captured:
case Decl::OMPDeclareReduction:
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 81f1c711269445..7ce37cf61164e9 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -5678,24 +5678,32 @@ Parser::DeclGroupPtrTy Parser::ParseTopLevelStmtDecl() {
// Parse a top-level-stmt.
Parser::StmtVector Stmts;
ParsedStmtContext SubStmtCtx = ParsedStmtContext();
- Actions.PushFunctionScope();
+ ParseScope FnScope(this, Scope::FnScope | Scope::DeclScope |
+ Scope::CompoundStmtScope);
+ TopLevelStmtDecl *TLSD = Actions.ActOnStartTopLevelStmtDecl(getCurScope());
StmtResult R = ParseStatementOrDeclaration(Stmts, SubStmtCtx);
- Actions.PopFunctionScopeInfo();
if (!R.isUsable())
return nullptr;
- SmallVector<Decl *, 2> DeclsInGroup;
- DeclsInGroup.push_back(Actions.ActOnTopLevelStmtDecl(R.get()));
+ Actions.ActOnFinishTopLevelStmtDecl(TLSD, R.get());
if (Tok.is(tok::annot_repl_input_end) &&
Tok.getAnnotationValue() != nullptr) {
ConsumeAnnotationToken();
- cast<TopLevelStmtDecl>(DeclsInGroup.back())->setSemiMissing();
+ TLSD->setSemiMissing();
}
- // Currently happens for things like -fms-extensions and use `__if_exists`.
- for (Stmt *S : Stmts)
- DeclsInGroup.push_back(Actions.ActOnTopLevelStmtDecl(S));
+ SmallVector<Decl *, 2> DeclsInGroup;
+ DeclsInGroup.push_back(TLSD);
+
+ // Currently happens for things like -fms-extensions and use `__if_exists`.
+ for (Stmt *S : Stmts) {
+ // Here we should be safe as `__if_exists` and friends are not introducing
+ // new variables which need to live outside file scope.
+ TopLevelStmtDecl *D = Actions.ActOnStartTopLevelStmtDecl(getCurScope());
+ Actions.ActOnFinishTopLevelStmtDecl(D, S);
+ DeclsInGroup.push_back(D);
+ }
return Actions.BuildDeclaratorGroup(DeclsInGroup);
}
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 6b81ee183cc440..b8177e47e4aa5c 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -20519,12 +20519,19 @@ Decl *Sema::ActOnFileScopeAsmDecl(Expr *expr,
return New;
}
-Decl *Sema::ActOnTopLevelStmtDecl(Stmt *Statement) {
- auto *New = TopLevelStmtDecl::Create(Context, Statement);
- Context.getTranslationUnitDecl()->addDecl(New);
+TopLevelStmtDecl *Sema::ActOnStartTopLevelStmtDecl(Scope *S) {
+ auto *New = TopLevelStmtDecl::Create(Context, /*Statement=*/nullptr);
+ PushFunctionScope();
+ PushDeclContext(S, New);
return New;
}
+void Sema::ActOnFinishTopLevelStmtDecl(TopLevelStmtDecl* D, Stmt *Statement) {
+ D->setStmt(Statement);
+ PopDeclContext();
+ PopFunctionScopeInfo();
+}
+
void Sema::ActOnPragmaRedefineExtname(IdentifierInfo* Name,
IdentifierInfo* AliasName,
SourceLocation PragmaLoc,
diff --git a/clang/test/Interpreter/execute-stmts.cpp b/clang/test/Interpreter/execute-stmts.cpp
index 34a2a35c0027d8..912b180849ef8b 100644
--- a/clang/test/Interpreter/execute-stmts.cpp
+++ b/clang/test/Interpreter/execute-stmts.cpp
@@ -49,3 +49,13 @@ if (int i = j) printf("i = %d\n", i);
for (int i = j; i > 3; --i) printf("i = %d\n", i);
// CHECK-NEXT: i = 4
+
+for(int i=0; i<2; i+=1) {};
+
+for(int i=0; i<2; i+=1) ;
+
+int a = 2;
+for(int i=0; i<2; i+=1) a-=1;
+
+int *aa=nullptr;
+if (auto *b=aa) *b += 1;
>From ff26b1273e0cc5e2d04144434aaaedff9aad9330 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Tue, 5 Mar 2024 13:58:46 +0100
Subject: [PATCH 3/7] fixup! Try to fix llvm/llvm-project#83028
Drop duplicate test-case
---
clang/test/Interpreter/execute-stmts.cpp | 3 ---
1 file changed, 3 deletions(-)
diff --git a/clang/test/Interpreter/execute-stmts.cpp b/clang/test/Interpreter/execute-stmts.cpp
index 912b180849ef8b..d339b3506e84a6 100644
--- a/clang/test/Interpreter/execute-stmts.cpp
+++ b/clang/test/Interpreter/execute-stmts.cpp
@@ -54,8 +54,5 @@ for(int i=0; i<2; i+=1) {};
for(int i=0; i<2; i+=1) ;
-int a = 2;
-for(int i=0; i<2; i+=1) a-=1;
-
int *aa=nullptr;
if (auto *b=aa) *b += 1;
>From 5aa374d4453a8ac82eda7605f248c275b571e7c0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Wed, 6 Mar 2024 11:35:37 +0100
Subject: [PATCH 4/7] fixup! Try to fix llvm/llvm-project#83028
clang-format
---
clang/include/clang/AST/Decl.h | 6 +++---
clang/include/clang/Sema/Sema.h | 2 +-
clang/lib/Parse/ParseDecl.cpp | 2 +-
clang/lib/Sema/SemaDecl.cpp | 2 +-
4 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index bca9d42c7e0586..a5879591f4c659 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -4427,7 +4427,7 @@ class TopLevelStmtDecl : public Decl, public DeclContext {
bool IsSemiMissing = false;
TopLevelStmtDecl(DeclContext *DC, SourceLocation L, Stmt *S)
- : Decl(TopLevelStmt, DC, L), DeclContext(TopLevelStmt), Statement(S) {}
+ : Decl(TopLevelStmt, DC, L), DeclContext(TopLevelStmt), Statement(S) {}
virtual void anchor();
@@ -4446,10 +4446,10 @@ class TopLevelStmtDecl : public Decl, public DeclContext {
static bool classofKind(Kind K) { return K == TopLevelStmt; }
static DeclContext *castToDeclContext(const TopLevelStmtDecl *D) {
- return static_cast<DeclContext *>(const_cast<TopLevelStmtDecl*>(D));
+ return static_cast<DeclContext *>(const_cast<TopLevelStmtDecl *>(D));
}
static TopLevelStmtDecl *castFromDeclContext(const DeclContext *DC) {
- return static_cast<TopLevelStmtDecl *>(const_cast<DeclContext*>(DC));
+ return static_cast<TopLevelStmtDecl *>(const_cast<DeclContext *>(DC));
}
};
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index a79f3c0786ce47..464649aee8a26b 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3264,7 +3264,7 @@ class Sema final {
SourceLocation RParenLoc);
TopLevelStmtDecl *ActOnStartTopLevelStmtDecl(Scope *S);
- void ActOnFinishTopLevelStmtDecl(TopLevelStmtDecl* D, Stmt *Statement);
+ void ActOnFinishTopLevelStmtDecl(TopLevelStmtDecl *D, Stmt *Statement);
void ActOnPopScope(SourceLocation Loc, Scope *S);
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 7ce37cf61164e9..64b234eb460d24 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -5679,7 +5679,7 @@ Parser::DeclGroupPtrTy Parser::ParseTopLevelStmtDecl() {
Parser::StmtVector Stmts;
ParsedStmtContext SubStmtCtx = ParsedStmtContext();
ParseScope FnScope(this, Scope::FnScope | Scope::DeclScope |
- Scope::CompoundStmtScope);
+ Scope::CompoundStmtScope);
TopLevelStmtDecl *TLSD = Actions.ActOnStartTopLevelStmtDecl(getCurScope());
StmtResult R = ParseStatementOrDeclaration(Stmts, SubStmtCtx);
if (!R.isUsable())
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index b8177e47e4aa5c..7227f26d5462d3 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -20526,7 +20526,7 @@ TopLevelStmtDecl *Sema::ActOnStartTopLevelStmtDecl(Scope *S) {
return New;
}
-void Sema::ActOnFinishTopLevelStmtDecl(TopLevelStmtDecl* D, Stmt *Statement) {
+void Sema::ActOnFinishTopLevelStmtDecl(TopLevelStmtDecl *D, Stmt *Statement) {
D->setStmt(Statement);
PopDeclContext();
PopFunctionScopeInfo();
>From 83265cf0101b48b222b40e3a0d3a9ee26234f8f0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Tue, 5 Mar 2024 13:58:19 +0100
Subject: [PATCH 5/7] fixup! Try to fix llvm/llvm-project#83028
ActOnForStmt() requires a CompoundScope when processing a NullStmt body
---
clang/lib/Sema/SemaDecl.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 7227f26d5462d3..c30bdf3e7fb488 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -20522,6 +20522,7 @@ Decl *Sema::ActOnFileScopeAsmDecl(Expr *expr,
TopLevelStmtDecl *Sema::ActOnStartTopLevelStmtDecl(Scope *S) {
auto *New = TopLevelStmtDecl::Create(Context, /*Statement=*/nullptr);
PushFunctionScope();
+ PushCompoundScope(false);
PushDeclContext(S, New);
return New;
}
@@ -20529,6 +20530,7 @@ TopLevelStmtDecl *Sema::ActOnStartTopLevelStmtDecl(Scope *S) {
void Sema::ActOnFinishTopLevelStmtDecl(TopLevelStmtDecl *D, Stmt *Statement) {
D->setStmt(Statement);
PopDeclContext();
+ PopCompoundScope();
PopFunctionScopeInfo();
}
>From 27341ab72c084ee0264ea5bbee025ff8c4d313d5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Wed, 6 Mar 2024 11:18:42 +0100
Subject: [PATCH 6/7] fixup! Try to fix llvm/llvm-project#83028
Add TopLevelStmtDecl to outer DeclContext (and push new scopes afterwards)
---
clang/lib/Sema/SemaDecl.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index c30bdf3e7fb488..67e56a917a51de 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -20521,17 +20521,18 @@ Decl *Sema::ActOnFileScopeAsmDecl(Expr *expr,
TopLevelStmtDecl *Sema::ActOnStartTopLevelStmtDecl(Scope *S) {
auto *New = TopLevelStmtDecl::Create(Context, /*Statement=*/nullptr);
+ CurContext->addDecl(New);
+ PushDeclContext(S, New);
PushFunctionScope();
PushCompoundScope(false);
- PushDeclContext(S, New);
return New;
}
void Sema::ActOnFinishTopLevelStmtDecl(TopLevelStmtDecl *D, Stmt *Statement) {
D->setStmt(Statement);
- PopDeclContext();
PopCompoundScope();
PopFunctionScopeInfo();
+ PopDeclContext();
}
void Sema::ActOnPragmaRedefineExtname(IdentifierInfo* Name,
>From 68ed18f4aa66b57d0c9efd103f82aa615db8b824 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Wed, 6 Mar 2024 11:30:16 +0100
Subject: [PATCH 7/7] Test cases for variable definitions inside for-init and
condition statements pass now
---
clang/test/Interpreter/execute-stmts.cpp | 3 ---
1 file changed, 3 deletions(-)
diff --git a/clang/test/Interpreter/execute-stmts.cpp b/clang/test/Interpreter/execute-stmts.cpp
index d339b3506e84a6..e341b06d154f18 100644
--- a/clang/test/Interpreter/execute-stmts.cpp
+++ b/clang/test/Interpreter/execute-stmts.cpp
@@ -9,9 +9,6 @@
//CODEGEN-CHECK-COUNT-2: define internal void @__stmts__
//CODEGEN-CHECK-NOT: define internal void @__stmts__
-// New tests fail right now
-// XFAIL: *
-
extern "C" int printf(const char*,...);
template <typename T> T call() { printf("called\n"); return T(); }
More information about the cfe-commits
mailing list