[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