[clang] 89d9912 - [AST] dont invaliate VarDecl when the initializer contains errors.
Haojian Wu via cfe-commits
cfe-commits at lists.llvm.org
Tue Apr 21 01:53:46 PDT 2020
Author: Haojian Wu
Date: 2020-04-21T10:53:35+02:00
New Revision: 89d9912cbf45068770ac8c1e2ef97b74c3b662ab
URL: https://github.com/llvm/llvm-project/commit/89d9912cbf45068770ac8c1e2ef97b74c3b662ab
DIFF: https://github.com/llvm/llvm-project/commit/89d9912cbf45068770ac8c1e2ef97b74c3b662ab.diff
LOG: [AST] dont invaliate VarDecl when the initializer contains errors.
Summary:
This patch contains 2 separate changes:
1) the initializer of a variable should play no part in decl "invalid" bit;
2) preserve the invalid initializer via recovery exprs;
With 1), we will regress the diagnostics (one big regression is that we loose
the "selected 'begin' function with iterator type" diagnostic in for-range stmt;
but with 2) together, we don't have regressions (the new diagnostics seems to be
improved).
Reviewers: sammccall
Reviewed By: sammccall
Subscribers: cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D78116
Added:
clang/test/SemaCXX/recovery-initializer.cpp
Modified:
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaStmt.cpp
clang/test/AST/ast-dump-invalid-initialized.cpp
clang/test/AST/ast-dump-recovery.cpp
clang/test/CXX/special/class.copy/p11.0x.move.cpp
clang/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp
clang/test/OpenMP/task_messages.cpp
clang/test/SemaCXX/block-call.cpp
clang/test/SemaCXX/constant-expression-cxx11.cpp
clang/test/SemaCXX/cxx11-crashes.cpp
clang/test/SemaCXX/cxx2a-explicit-bool.cpp
clang/test/SemaCXX/for-range-dereference.cpp
clang/test/SemaCXX/member-init.cpp
clang/test/SemaObjCXX/parameterized_classes_arc.mm
Removed:
################################################################################
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 6241162fd992..c90c840a5e24 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -11996,7 +11996,12 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) {
/*TreatUnavailableAsInvalid=*/false);
ExprResult Result = InitSeq.Perform(*this, Entity, Kind, Args, &DclT);
if (Result.isInvalid()) {
- VDecl->setInvalidDecl();
+ // If the provied initializer fails to initialize the var decl,
+ // we attach a recovery expr for better recovery.
+ auto RecoveryExpr =
+ CreateRecoveryExpr(Init->getBeginLoc(), Init->getEndLoc(), Args);
+ if (RecoveryExpr.get())
+ VDecl->setInit(RecoveryExpr.get());
return;
}
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 21cca3d04325..291f7641c910 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -2669,7 +2669,8 @@ StmtResult Sema::BuildCXXForRangeStmt(SourceLocation ForLoc,
// trying to determine whether this would be a valid range.
if (!LoopVar->isInvalidDecl() && Kind != BFRK_Check) {
AddInitializerToDecl(LoopVar, DerefExpr.get(), /*DirectInit=*/false);
- if (LoopVar->isInvalidDecl())
+ if (LoopVar->isInvalidDecl() ||
+ (LoopVar->getInit() && LoopVar->getInit()->containsErrors()))
NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin);
}
}
diff --git a/clang/test/AST/ast-dump-invalid-initialized.cpp b/clang/test/AST/ast-dump-invalid-initialized.cpp
index 800a79cc831b..1b3c8581a94f 100644
--- a/clang/test/AST/ast-dump-invalid-initialized.cpp
+++ b/clang/test/AST/ast-dump-invalid-initialized.cpp
@@ -4,6 +4,8 @@ struct A { A(int, int) {} };
class ForwardDecl;
void test() {
+ // Verify the valid-bit of the VarDecl.
+
// CHECK: `-VarDecl {{.*}} a1 'A'
A a1;
// CHECK: `-VarDecl {{.*}} a2 'const A'
@@ -16,4 +18,10 @@ void test() {
const A& b1;
// CHECK: `-VarDecl {{.*}} invalid b2 'ForwardDecl'
ForwardDecl b2;
+ // CHECK: `-VarDecl {{.*}} invalid b3 'auto'
+ auto b3 = garbage();
+ // CHECK: `-VarDecl {{.*}} invalid b4 'auto'
+ auto b4 = A(1);
+ // CHECK: `-VarDecl {{.*}} invalid b5 'auto'
+ auto b5 = A{1};
}
\ No newline at end of file
diff --git a/clang/test/AST/ast-dump-recovery.cpp b/clang/test/AST/ast-dump-recovery.cpp
index 248470a5093c..86511181f0a6 100644
--- a/clang/test/AST/ast-dump-recovery.cpp
+++ b/clang/test/AST/ast-dump-recovery.cpp
@@ -103,3 +103,56 @@ void test(int x) {
// CHECK-NEXT:| `-RecoveryExpr {{.*}} contains-errors
// CHECK-NEXT:| `-UnresolvedLookupExpr {{.*}} 'invalid'
struct alignas(invalid()) Aligned {};
+
+void InvalidInitalizer(int x) {
+ struct Bar { Bar(); };
+ // CHECK: `-VarDecl {{.*}} a1 'Bar'
+ // CHECK-NEXT: `-RecoveryExpr {{.*}} contains-errors
+ // CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 1
+ Bar a1(1);
+ // CHECK: `-VarDecl {{.*}} a2 'Bar'
+ // CHECK-NEXT: `-RecoveryExpr {{.*}} contains-errors
+ // CHECK-NEXT: `-DeclRefExpr {{.*}} 'x'
+ Bar a2(x);
+ // CHECK: `-VarDecl {{.*}} a3 'Bar'
+ // CHECK-NEXT: `-RecoveryExpr {{.*}} contains-errors
+ // CHECK-NEXT: `-InitListExpr
+ // CHECK-NEDT: `-DeclRefExpr {{.*}} 'x'
+ Bar a3{x};
+ // CHECK: `-VarDecl {{.*}} a4 'Bar'
+ // CHECK-NEXT: `-ParenListExpr {{.*}} 'NULL TYPE' contains-errors
+ // CHECK-NEXT: `-RecoveryExpr {{.*}} contains-errors
+ // CHECK-NEXT: `-UnresolvedLookupExpr {{.*}} 'invalid'
+ Bar a4(invalid());
+ // CHECK: `-VarDecl {{.*}} a5 'Bar'
+ // CHECK-NEXT: `-InitListExpr {{.*}} contains-errors
+ // CHECK-NEXT: `-RecoveryExpr {{.*}} contains-errors
+ // CHECK-NEXT: `-UnresolvedLookupExpr {{.*}} 'invalid'
+ Bar a5{invalid()};
+
+ // CHECK: `-VarDecl {{.*}} b1 'Bar'
+ // CHECK-NEXT: `-RecoveryExpr {{.*}} contains-errors
+ // CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 1
+ Bar b1 = 1;
+ // CHECK: `-VarDecl {{.*}} b2 'Bar'
+ // CHECK-NEXT: `-RecoveryExpr {{.*}} contains-errors
+ // CHECK-NEXT: `-InitListExpr
+ Bar b2 = {1};
+ // FIXME: preserve the invalid initializer.
+ // CHECK: `-VarDecl {{.*}} b3 'Bar'
+ Bar b3 = Bar(x);
+ // FIXME: preserve the invalid initializer.
+ // CHECK: `-VarDecl {{.*}} b4 'Bar'
+ Bar b4 = Bar{x};
+ // CHECK: `-VarDecl {{.*}} b5 'Bar'
+ // CHECK-NEXT: `-CXXUnresolvedConstructExpr {{.*}} 'Bar' contains-errors 'Bar'
+ // CHECK-NEXT: `-RecoveryExpr {{.*}} contains-errors
+ // CHECK-NEXT: `-UnresolvedLookupExpr {{.*}} 'invalid'
+ Bar b5 = Bar(invalid());
+ // CHECK: `-VarDecl {{.*}} b6 'Bar'
+ // CHECK-NEXT: `-CXXUnresolvedConstructExpr {{.*}} 'Bar' contains-errors 'Bar'
+ // CHECK-NEXT: `-InitListExpr {{.*}} contains-errors
+ // CHECK-NEXT: `-RecoveryExpr {{.*}} contains-errors
+ // CHECK-NEXT: `-UnresolvedLookupExpr {{.*}} 'invalid'
+ Bar b6 = Bar{invalid()};
+}
diff --git a/clang/test/CXX/special/class.copy/p11.0x.move.cpp b/clang/test/CXX/special/class.copy/p11.0x.move.cpp
index 5b016836e9ef..b0df0ee7739c 100644
--- a/clang/test/CXX/special/class.copy/p11.0x.move.cpp
+++ b/clang/test/CXX/special/class.copy/p11.0x.move.cpp
@@ -32,7 +32,7 @@ template<typename T> struct DeletedNTVariant3 { // expected-note 2{{default}} ex
};
};
extern DeletedNTVariant3<NonTrivial> dntv3a(0); // expected-error {{no matching}}
-extern DeletedNTVariant3<DeletedCopy> dntv3a(0); // expected-error {{no matching}}
+extern DeletedNTVariant3<DeletedCopy> dntv3b(0); // expected-error {{no matching}}
// -- a non-static data member of class type M (or array thereof) that cannot be
// copied because overload resolution results in an ambiguity or a function
diff --git a/clang/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp b/clang/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp
index f42fc9b6e5aa..494f001a9cde 100644
--- a/clang/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp
+++ b/clang/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp
@@ -21,8 +21,7 @@ namespace X {
auto end(T &&t) -> decltype(t.end()) { return t.end(); } // expected-note {{candidate template ignored: substitution failure [with T = }}
template<typename T>
- auto begin(T &&t) -> decltype(t.alt_begin()) { return t.alt_begin(); } // expected-note {{selected 'begin' template [with T = }} \
- expected-note 2{{candidate template ignored: substitution failure [with T = }}
+ auto begin(T &&t) -> decltype(t.alt_begin()) { return t.alt_begin(); } // expected-note 2{{candidate template ignored: substitution failure [with T = }}
template<typename T>
auto end(T &&t) -> decltype(t.alt_end()) { return t.alt_end(); } // expected-note {{candidate template ignored: substitution failure [with T = }}
@@ -36,7 +35,7 @@ namespace X {
struct A { // expected-note 2 {{candidate constructor}}
A();
- int *begin(); // expected-note 3{{selected 'begin' function with iterator type 'int *'}} expected-note {{'begin' declared here}}
+ int *begin(); // expected-note {{selected 'begin' function with iterator type 'int *'}} expected-note {{'begin' declared here}}
int *end();
};
diff --git a/clang/test/OpenMP/task_messages.cpp b/clang/test/OpenMP/task_messages.cpp
index ccead8477573..8b3183e0bd93 100644
--- a/clang/test/OpenMP/task_messages.cpp
+++ b/clang/test/OpenMP/task_messages.cpp
@@ -48,7 +48,6 @@ int foo() {
S1 s1;
// expected-error at +1 2 {{call to deleted constructor of 'S1'}}
#pragma omp task
-// expected-note at +1 2 {{predetermined as a firstprivate in a task construct here}}
++s1;
#pragma omp task default(none) // expected-note 2 {{explicit data sharing attribute requested here}}
#pragma omp task default(shared)
diff --git a/clang/test/SemaCXX/block-call.cpp b/clang/test/SemaCXX/block-call.cpp
index d519911589a7..4c8234d7fcf1 100644
--- a/clang/test/SemaCXX/block-call.cpp
+++ b/clang/test/SemaCXX/block-call.cpp
@@ -10,8 +10,6 @@ int main() {
int (^PFR) (int) = IFP; // expected-error {{cannot initialize a variable of type 'int (^)(int)' with an lvalue of type 'int (^)()'}}
PFR = II; // OK
- int (^IFP) () = PFR; // OK
-
const int (^CIC) () = IFP; // OK - initializing 'const int (^)()' with an expression of type 'int (^)()'}}
@@ -32,8 +30,8 @@ int main() {
int (^IPCC6) (int, char (^CArg) (float)) = IPCC4; // expected-error {{cannot initialize a variable of type 'int (^)(int, char (^)(float))' with an lvalue of type}}
- IPCC2 = 0;
- IPCC2 = 1;
+ IPCC2 = 0; // OK - assign a nullptr to a pointer.
+ IPCC2 = 1; // expected-error {{invalid block pointer conversion assigning to 'int *(^)()' from 'int'}}
int (^x)() = 0;
int (^y)() = 3; // expected-error {{cannot initialize a variable of type 'int (^)()' with an rvalue of type 'int'}}
int a = 1;
diff --git a/clang/test/SemaCXX/constant-expression-cxx11.cpp b/clang/test/SemaCXX/constant-expression-cxx11.cpp
index 4c0cf109bee0..a8ded62e88ee 100644
--- a/clang/test/SemaCXX/constant-expression-cxx11.cpp
+++ b/clang/test/SemaCXX/constant-expression-cxx11.cpp
@@ -877,9 +877,9 @@ static_assert((Derived*)(Base*)pb1 == (Derived*)pok2, "");
// null pointer in C++11. Just check for an integer literal with value 0.
constexpr Base *nullB = 42 - 6 * 7; // expected-error {{cannot initialize a variable of type 'Class::Base *const' with an rvalue of type 'int'}}
constexpr Base *nullB1 = 0;
-static_assert((Bottom*)nullB == 0, "");
-static_assert((Derived*)nullB == 0, "");
-static_assert((void*)(Bottom*)nullB == (void*)(Derived*)nullB, "");
+static_assert((Bottom*)nullB == 0, ""); // expected-error {{static_assert expression is not an integral constant expression}}
+static_assert((Derived*)nullB1 == 0, "");
+static_assert((void*)(Bottom*)nullB1 == (void*)(Derived*)nullB1, "");
Base *nullB2 = '\0'; // expected-error {{cannot initialize a variable of type 'Class::Base *' with an rvalue of type 'char'}}
Base *nullB3 = (0);
Base *nullB4 = false; // expected-error {{cannot initialize a variable of type 'Class::Base *' with an rvalue of type 'bool'}}
diff --git a/clang/test/SemaCXX/cxx11-crashes.cpp b/clang/test/SemaCXX/cxx11-crashes.cpp
index 7c455eecd5fa..19205db85b6c 100644
--- a/clang/test/SemaCXX/cxx11-crashes.cpp
+++ b/clang/test/SemaCXX/cxx11-crashes.cpp
@@ -67,7 +67,7 @@ namespace b6981007 {
struct S {}; // expected-note 3{{candidate}}
void f() {
S s(1, 2, 3); // expected-error {{no matching}}
- for (auto x : s) {
+ for (auto x : s) { // expected-error {{invalid range expression of}}
// We used to attempt to evaluate the initializer of this variable,
// and crash because it has an undeduced type.
const int &n(x);
diff --git a/clang/test/SemaCXX/cxx2a-explicit-bool.cpp b/clang/test/SemaCXX/cxx2a-explicit-bool.cpp
index 45385972cab8..c9e960f2d3db 100644
--- a/clang/test/SemaCXX/cxx2a-explicit-bool.cpp
+++ b/clang/test/SemaCXX/cxx2a-explicit-bool.cpp
@@ -124,7 +124,7 @@ A<true> && a5 = { 0};// expected-error {{chosen constructor is explicit}}
A<true> && a6{ 0};
A<true> a7 = { 0}; // expected-error {{chosen constructor is explicit in copy-initialization}}
-a0 = 0;
+a0 = 0; // expected-error {{no viable overloaded '='}}
a1 = { 0}; // expected-error {{no viable overloaded '='}}
a2 = A<true>( 0);
a3 = A<true>{ 0};
diff --git a/clang/test/SemaCXX/for-range-dereference.cpp b/clang/test/SemaCXX/for-range-dereference.cpp
index de4958c31bce..bd8bb5717362 100644
--- a/clang/test/SemaCXX/for-range-dereference.cpp
+++ b/clang/test/SemaCXX/for-range-dereference.cpp
@@ -85,5 +85,4 @@ expected-note {{when looking up 'end' function for range expression of type 'Del
for (Data *p : pt) { } // expected-error {{invalid range expression of type 'T *'; did you mean to dereference it with '*'?}}
// expected-error at -1 {{no viable conversion from 'Data' to 'Data *'}}
- // expected-note at 4 {{selected 'begin' function with iterator type 'Data *'}}
}
diff --git a/clang/test/SemaCXX/member-init.cpp b/clang/test/SemaCXX/member-init.cpp
index ff5dadea16e6..33f16940bbf8 100644
--- a/clang/test/SemaCXX/member-init.cpp
+++ b/clang/test/SemaCXX/member-init.cpp
@@ -64,7 +64,7 @@ namespace PR10578 {
template<typename T>
struct X {
X() {
- T* x = 1; // expected-error{{cannot initialize a variable of type 'int *' with an rvalue of type 'int'}}
+ T* x = 1; // expected-error{{cannot initialize a variable of type 'int *' with an rvalue of type 'int'}} expected-warning {{unused variable}}
}
};
diff --git a/clang/test/SemaCXX/recovery-initializer.cpp b/clang/test/SemaCXX/recovery-initializer.cpp
new file mode 100644
index 000000000000..e1f35dbc1f93
--- /dev/null
+++ b/clang/test/SemaCXX/recovery-initializer.cpp
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -frecovery-ast -verify %s
+
+// NOTE: these tests can be merged to existing tests after -frecovery-ast is
+// turned on by default.
+void test1() {
+ struct Data {};
+ struct T {
+ Data *begin();
+ Data *end();
+ };
+ T *pt;
+ for (Data *p : T()) {} // expected-error {{no viable conversion from 'Data' to 'Data *'}}
+ // expected-note at -5 {{selected 'begin' function with iterator type}}
+}
+
+void test2() {
+ struct Bottom {
+ constexpr Bottom() {}
+ };
+ struct Base : Bottom {
+ constexpr Base(int a = 42, const char *b = "test") : a(a), b(b) {}
+ int a;
+ const char *b;
+ };
+ constexpr Base *nullB = 12; // expected-error {{cannot initialize a variable of type}}
+ // verify that the "static_assert expression is not an integral constant expr"
+ // diagnostic is suppressed.
+ static_assert((Bottom*)nullB == 0, "");
+}
diff --git a/clang/test/SemaObjCXX/parameterized_classes_arc.mm b/clang/test/SemaObjCXX/parameterized_classes_arc.mm
index 38ddd70986eb..c7560e72b720 100644
--- a/clang/test/SemaObjCXX/parameterized_classes_arc.mm
+++ b/clang/test/SemaObjCXX/parameterized_classes_arc.mm
@@ -13,7 +13,7 @@ @interface NSObject
@interface PC1<T> : NSObject
- (T) get;
-- (void) set: (T) v;
+- (void) set: (T) v; // expected-note 4{{passing argument to}}
@end
void test1a(PC1<__weak id> *obj) { // expected-error {{type argument '__weak id' cannot be qualified with '__weak'}}
@@ -34,17 +34,17 @@ void test1c(PC1<id> *obj) {
// Test that this doesn't completely kill downstream type-checking.
void test1d(PC1<__weak Forward*> *obj) { // expected-error {{type argument 'Forward *__weak' cannot be qualified with '__weak'}}
Forward2 *x = [obj get]; // expected-error {{cannot initialize}}
- [obj set: x];
+ [obj set: x]; // expected-error {{cannot initialize a parameter of type 'Forward *' with an lvalue of type 'Forward2 *__strong'}}
}
void test1e(PC1<__strong Forward*> *obj) { // expected-error {{type argument 'Forward *__strong' cannot be qualified with '__strong'}}
Forward2 *x = [obj get]; // expected-error {{cannot initialize}}
- [obj set: x];
+ [obj set: x]; // expected-error {{cannot initialize a parameter of type 'Forward *'}}
}
void test1f(PC1<Forward*> *obj) {
Forward2 *x = [obj get]; // expected-error {{cannot initialize}}
- [obj set: x];
+ [obj set: x]; // expected-error {{cannot initialize a parameter of type 'Forward *'}}
}
// Typedefs are fine, just silently ignore them.
@@ -57,7 +57,7 @@ void test1g(PC1<StrongID> *obj) {
typedef __strong Forward *StrongForward;
void test1h(PC1<StrongForward> *obj) {
Forward2 *x = [obj get]; // expected-error {{cannot initialize}}
- [obj set: x];
+ [obj set: x]; // expected-error {{cannot initialize a parameter of type 'Forward *'}}
}
// These aren't really ARC-specific, but they're the same basic idea.
More information about the cfe-commits
mailing list