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@+1 2 {{call to deleted constructor of 'S1'}} #pragma omp task -// expected-note@+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@-1 {{no viable conversion from 'Data' to 'Data *'}} - // expected-note@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@-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. _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits