Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -4811,7 +4811,17 @@ def ext_typecheck_comparison_of_distinct_pointers_nonstandard : ExtWarn< "comparison of distinct pointer types (%0 and %1) uses non-standard " "composite pointer type %2">, InGroup; + def err_typecheck_assign_const : Error<"read-only variable is not assignable">; +def note_function_returns_const_ref : Note< + "function %0 returns %1 which is a const reference">; +def note_const_variable : Note< + "variable %0 is declared here with type %1 which is const">; +def note_const_field : Note< + "field %0 is declared here with type %1 which is const">; +def note_const_method : Note< + "method %0 is declared const here">; + def err_stmtexpr_file_scope : Error< "statement expression not allowed at file scope">; def warn_mixed_sign_comparison : Warning< Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -8482,6 +8482,60 @@ return (isa(DC) ? NCCK_Block : NCCK_Lambda); } +// Add note(s) after a "read-only variable not assignable" error to give +// more information about why the variable is not assignable, such as pointing +// to the declaration of a const variable, showing that a method is const, or +// that the funciton is returning a const reference. +static void AddNotesForConstAssignment(Sema &S, const Expr *E) { + E = E->IgnoreParens(); + + // If a call expression is found, point to function declaraion. + if (const CallExpr *CE = dyn_cast(E)) { + if (const FunctionDecl *FD = CE->getDirectCallee()) { + if (FD) { + QualType ReturnType = FD->getReturnType(); + S.Diag(FD->getReturnTypeSourceRange().getBegin(), + diag::note_function_returns_const_ref) + << FD << ReturnType << FD->getReturnTypeSourceRange(); + } + } + } + + // Point to variable declaration. + if (const DeclRefExpr *DRE = dyn_cast(E)) { + const ValueDecl *VD = DRE->getDecl(); + if (VD && VD->getType().isConstQualified()) + S.Diag(VD->getLocation(), diag::note_const_variable) + << VD << VD->getType() << VD->getSourceRange(); + } + + // Handle class fields and methods. + if (const MemberExpr *ME = dyn_cast(E)) { + const Expr *Base = ME; + while (const MemberExpr *Member = dyn_cast(Base)) { + ValueDecl *VD = Member->getMemberDecl(); + if (VD->getType().isConstQualified()) { + // Class field is const. + S.Diag(VD->getLocation(), diag::note_const_field) + << VD << VD->getType() << VD->getSourceRange(); + } + Base = Member->getBase(); + } + + // Class method is const. + if (isa(Base)) { + if (const DeclContext *DC = S.getFunctionLevelDeclContext()) { + if (const CXXMethodDecl* MD = dyn_cast(DC)) { + if (MD->isConst()) + S.Diag(MD->getLocation(), diag::note_const_method) + << MD << MD->getSourceRange(); + } + } + } + } +} + + /// CheckForModifiableLvalue - Verify that E is a modifiable lvalue. If not, /// emit an error and return true. If so, return false. static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) { @@ -8589,6 +8643,10 @@ S.Diag(Loc, Diag) << E->getType() << E->getSourceRange() << Assign; else S.Diag(Loc, Diag) << E->getSourceRange() << Assign; + + if (Diag == diag::err_typecheck_assign_const) + AddNotesForConstAssignment(S, E); + return true; } Index: test/CXX/temp/temp.decls/temp.mem/p5.cpp =================================================================== --- test/CXX/temp/temp.decls/temp.mem/p5.cpp +++ test/CXX/temp/temp.decls/temp.mem/p5.cpp @@ -55,7 +55,7 @@ // Partial ordering with conversion function templates. struct X0 { template operator T*() { - T x = 1; + T x = 1; // expected-note{{variable 'x' is declared here with type 'const int' which is const}} x = 17; // expected-error{{read-only variable is not assignable}} } Index: test/Sema/block-misc.c =================================================================== --- test/Sema/block-misc.c +++ test/Sema/block-misc.c @@ -184,7 +184,7 @@ } void test18() { - void (^const blockA)(void) = ^{ }; + void (^const blockA)(void) = ^{ }; // expected-note {{variable 'blockA' is declared here with type 'void (^const)(void)' which is const}} blockA = ^{ }; // expected-error {{read-only variable is not assignable}} } Index: test/SemaCXX/anonymous-union.cpp =================================================================== --- test/SemaCXX/anonymous-union.cpp +++ test/SemaCXX/anonymous-union.cpp @@ -39,7 +39,7 @@ a = 0; } -void X::test_unqual_references_const() const { +void X::test_unqual_references_const() const { // expected-note 2{{method 'test_unqual_references_const' is declared const here}} d = 0.0; f2 = 0; // expected-error{{read-only variable is not assignable}} a = 0; // expected-error{{read-only variable is not assignable}} Index: test/SemaCXX/captured-statements.cpp =================================================================== --- test/SemaCXX/captured-statements.cpp +++ test/SemaCXX/captured-statements.cpp @@ -81,7 +81,7 @@ } template -S template_capture_var(S x, T y) { +S template_capture_var(S x, T y) { // expected-note{{variable 'y' is declared here with type 'const int' which is const}} #pragma clang _debug captured { x++; Index: test/SemaCXX/cxx0x-constexpr-const.cpp =================================================================== --- test/SemaCXX/cxx0x-constexpr-const.cpp +++ test/SemaCXX/cxx0x-constexpr-const.cpp @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s -constexpr int x = 1; +constexpr int x = 1; // expected-note {{variable 'x' is declared here with type 'const int' which is const}} constexpr int id(int x) { return x; } void foo(void) { Index: test/SemaCXX/err_typecheck_assign_const.cpp =================================================================== --- test/SemaCXX/err_typecheck_assign_const.cpp +++ test/SemaCXX/err_typecheck_assign_const.cpp @@ -0,0 +1,68 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s + +const int global = 5; // expected-note{{variable 'global' is declared here with type 'const int' which is const}} +void test1() { + global = 2; // expected-error{{read-only variable is not assignable}} +} + +void test2 () { + const int local = 5; // expected-note{{variable 'local' is declared here with type 'const int' which is const}} + local = 0; // expected-error{{read-only variable is not assignable}} +} + +void test2 (const int parameter) { // expected-note{{variable 'parameter' is declared here with type 'const int' which is const}} + parameter = 2; // expected-error{{read-only variable is not assignable}} +} + +class test3 { + int field; + const int const_field = 1; // expected-note 2{{field 'const_field' is declared here with type 'const int' which is const}} + static const int static_const_field = 1; // expected-note 2{{variable 'static_const_field' is declared here with type 'const int' which is const}} + void test() { + const_field = 4; // expected-error{{read-only variable is not assignable}} + static_const_field = 4; // expected-error{{read-only variable is not assignable}} + } + void test_const() const { // expected-note 2{{method 'test_const' is declared const here}} + field = 4; // expected-error{{read-only variable is not assignable}} + const_field =4 ; // expected-error{{read-only variable is not assignable}} + static_const_field = 4; // expected-error{{read-only variable is not assignable}} + } +}; + +const int &return_const_ref(); // expected-note{{function 'return_const_ref' returns 'const int &' which is a const reference}} + +void test4() { + return_const_ref() = 10; // expected-error{{read-only variable is not assignable}} +} + +struct S5 { + int field; + const int const_field = 4; // expected-note {{field 'const_field' is declared here with type 'const int' which is const}} +}; + +void test5() { + S5 s5; + s5.field = 5; + s5.const_field = 5; // expected-error{{read-only variable is not assignable}} +} + +struct U1 { + int a = 5; +}; + +struct U2 { + U1 u1; +}; + +struct U3 { + const U2 u2 = U2(); // expected-note{{field 'u2' is declared here with type 'const U2' which is const}} +}; + +struct U4 { + U3 u3; +}; + +void test6() { + U4 u4; + u4.u3.u2.u1.a = 5; // expected-error{{read-only variable is not assignable}} +} Index: test/SemaCXX/function-type-qual.cpp =================================================================== --- test/SemaCXX/function-type-qual.cpp +++ test/SemaCXX/function-type-qual.cpp @@ -17,7 +17,7 @@ x = 0; } - void m2() const { + void m2() const { // expected-note {{method 'm2' is declared const here}} x = 0; // expected-error {{read-only variable is not assignable}} } Index: test/SemaObjC/arc.m =================================================================== --- test/SemaObjC/arc.m +++ test/SemaObjC/arc.m @@ -293,7 +293,7 @@ x = 0; // expected-error {{fast enumeration variables can't be modified in ARC by default; declare the variable __strong to allow this}} } - for (const id x in collection) { + for (const id x in collection) { // expected-note {{variable 'x' is declared here with type 'const __strong id' which is const}} x = 0; // expected-error {{read-only variable is not assignable}} } Index: test/SemaTemplate/dependent-type-identity.cpp =================================================================== --- test/SemaTemplate/dependent-type-identity.cpp +++ test/SemaTemplate/dependent-type-identity.cpp @@ -111,7 +111,9 @@ void A::f(int x) { x = 0; } template - void A::g(const int x) { x = 0; } // expected-error {{not assignable}} + void A::g(const int x) { // expected-note {{declared here}} + x = 0; // expected-error {{not assignable}} + } template void A::h(T) {} // FIXME: Should reject this. Type is different from prior decl if T is an array type.