[PATCH] Add more information when displaying a "read-only variable is not assignable" error
Richard Trieu
rtrieu at google.com
Fri Feb 20 15:44:00 PST 2015
================
Comment at: lib/Sema/SemaExpr.cpp:8488
@@ +8487,3 @@
+/// to the declaration of a const variable, showing that a method is const, or
+/// that the funciton is returning a const reference.
+static void DiagnoseConstAssignment(Sema &S, const Expr *E,
----------------
rsmith wrote:
> Typo 'funciton'.
Fixed.
================
Comment at: lib/Sema/SemaExpr.cpp:8508
@@ +8507,3 @@
+
+ while (true) {
+ if (const MemberExpr *ME = dyn_cast<MemberExpr>(E)) {
----------------
rsmith wrote:
> Each iteration of this loop should `IgnoreParenImpCasts` or similar.
Call to `IgnoreParenImpsCasts` added.
================
Comment at: lib/Sema/SemaExpr.cpp:8514
@@ +8513,3 @@
+ if (Field->isMutable()) {
+ break;
+ }
----------------
rsmith wrote:
> Can you `assert(DiagnosticEmitted)` here?
Oops, will get this on next patch.
================
Comment at: lib/Sema/SemaExpr.cpp:8528
@@ +8527,3 @@
+ }
+ E = ME->getBase();
+ continue;
----------------
rsmith wrote:
> Do we need to track whether `.` or `->` was used here? If we have:
>
> struct A { const int n; };
> extern A *p const;
> p->n = 0;
>
> ... we shouldn't produce a note about `p`; its constness is not relevant. And conversely if we have:
>
> struct B { int n; };
> const B *q;
> q->n = 0;
>
> ... then we should produce a note about `q`.
Dereference is now tracked by `IsDereference` and `NextIsDereference`. Those examples have been added to the tests.
================
Comment at: lib/Sema/SemaExpr.cpp:8549-8570
@@ +8548,24 @@
+ // Member calls
+ if (const CXXMemberCallExpr *Call = dyn_cast<CXXMemberCallExpr>(E)) {
+ if (const MemberExpr *ME = dyn_cast<MemberExpr>(Call->getCallee())) {
+ if (const CXXMethodDecl *MD =
+ dyn_cast<CXXMethodDecl>(ME->getMemberDecl())) {
+ QualType ReturnType = MD->getReturnType();
+ if (ReturnType.getNonReferenceType().isConstQualified()) {
+ if (!DiagnosticEmitted) {
+ S.Diag(Loc, diag::err_typecheck_assign_const)
+ << ExprRange << ConstFunction << MD;
+ DiagnosticEmitted = true;
+ }
+ S.Diag(MD->getReturnTypeSourceRange().getBegin(),
+ diag::note_typecheck_assign_const)
+ << ConstFunction << MD << ReturnType
+ << MD->getReturnTypeSourceRange();
+ }
+ E = Call->getCallee();
+ continue;
+ }
+ }
+ break;
+ } // End CXXMemberCallExpr
+
----------------
rsmith wrote:
> I'm not sure this special case makes sense as it stands. If I have:
>
> struct A {};
> struct B {
> const A f();
> } const b;
> void g() {
> b.f() = A();
> }
>
> ... then it's not at all relevant that `b` is `const`, but I think I'll get a note telling me that it is. I think the appropriate check here is somewhat more sophisticated; if:
>
> * we have a member call on a non-static member function, and
> * that function is `const`, and
> * there is a "similar" function with the same name that is not `const`, and
> * the similar function has a non-`const` (or reference-to-non-`const`) return type
>
> ... then it matters that the object is `const` (and arguably we don't need to produce a note pointing at the member function).
>
> This seems like a good enhancement to the basic mechanism here, but my preference would be to drop the member call part of this patch and add that enhancement as a separate patch.
Testing:
```
struct A {};
struct B {
const A f() const;
} const b = {};
void g() {
b.f() = A();
}
```
The error message is no viable overload since the implicit move and copy assignment operators for A are not marked const, which is a different code path.
Testing:
```
struct B {
const int f() const;
} const b = {};
void g() {
b.f() = 5;
}
```
Gives: expression is not assignable, also a different code path.
Testing:
```
struct B {
const int& f() const;
} const b = {};
void g() {
b.f() = 5;
}
```
Gives:
```
const.cc:65:11: error: cannot assign to return value because function 'f'
returns a const value
b.f() = 5;
~~~~~ ^
const.cc:62:11: note: function 'f' which returns const-qualified type
'const int &' declared here
const int& f() const;
^~~~
1 error generated.
```
Which does not show a message for `b` at all.
================
Comment at: lib/Sema/SemaExpr.cpp:8575
@@ +8574,3 @@
+ const FunctionDecl *FD = CE->getDirectCallee();
+ if (FD->getReturnType().getNonReferenceType().isConstQualified()) {
+ if (!DiagnosticEmitted) {
----------------
rsmith wrote:
> Use `FD->getCallResultType().isConstQualified()` here. (I don't think there are any functionality changes from that, but if there are, they would be bugfixes.)
Call to `getNonReferenceType()` is needed otherwise
```
const int& foo();
foo() = 5;
```
would fall through to the default error message below.
================
Comment at: lib/Sema/SemaExpr.cpp:8587
@@ +8586,3 @@
+ }
+ E = CE->getCallee()->IgnoreImpCasts();
+ continue;
----------------
rsmith wrote:
> The constness of the callee doesn't seem relevant here; should probably just `break` at this point.
`break` added.
================
Comment at: lib/Sema/SemaExpr.cpp:8594
@@ +8593,3 @@
+ if (const ValueDecl *VD = DRE->getDecl()) {
+ if (VD->getType().isConstQualified()) {
+ if (!DiagnosticEmitted) {
----------------
rsmith wrote:
> It'd be nice to also handle reference-to-const here.
References are handled in `IsTypeReadable`
================
Comment at: lib/Sema/SemaExpr.cpp:8611-8615
@@ +8610,7 @@
+ if (MD->isConst()) {
+ if (!DiagnosticEmitted) {
+ S.Diag(Loc, diag::err_typecheck_assign_const)
+ << ExprRange << ConstMethod << MD;
+ DiagnosticEmitted = true;
+ }
+ S.Diag(MD->getLocation(), diag::note_typecheck_assign_const)
----------------
rsmith wrote:
> I worry that we'll diagnose "cannot assign to non-static data member within const member function" in response to:
>
> struct X { void f() { this = new X; } };
>
> As noted above, perhaps we should track whether we're assigning to the result of `E` or the result of dereferencing the result of `E`.
Dereferences are checked now. In this cases, assigning to `this` pointer causes a different error to be emitted:
```
const.cc:73:28: error: expression is not assignable
struct X { void f() { this = new X; } };
~~~~ ^
```
http://reviews.llvm.org/D4479
EMAIL PREFERENCES
http://reviews.llvm.org/settings/panel/emailpreferences/
More information about the cfe-commits
mailing list