[PATCH] Add more information when displaying a "read-only variable is not assignable" error
Richard Smith
richard at metafoo.co.uk
Wed Sep 24 18:09:48 PDT 2014
================
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,
----------------
Typo 'funciton'.
================
Comment at: lib/Sema/SemaExpr.cpp:8508
@@ +8507,3 @@
+
+ while (true) {
+ if (const MemberExpr *ME = dyn_cast<MemberExpr>(E)) {
----------------
Each iteration of this loop should `IgnoreParenImpCasts` or similar.
================
Comment at: lib/Sema/SemaExpr.cpp:8514
@@ +8513,3 @@
+ if (Field->isMutable()) {
+ break;
+ }
----------------
Can you `assert(DiagnosticEmitted)` here?
================
Comment at: lib/Sema/SemaExpr.cpp:8528
@@ +8527,3 @@
+ }
+ E = ME->getBase();
+ continue;
----------------
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`.
================
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
+
----------------
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.
================
Comment at: lib/Sema/SemaExpr.cpp:8575
@@ +8574,3 @@
+ const FunctionDecl *FD = CE->getDirectCallee();
+ if (FD->getReturnType().getNonReferenceType().isConstQualified()) {
+ if (!DiagnosticEmitted) {
----------------
Use `FD->getCallResultType().isConstQualified()` here. (I don't think there are any functionality changes from that, but if there are, they would be bugfixes.)
================
Comment at: lib/Sema/SemaExpr.cpp:8587
@@ +8586,3 @@
+ }
+ E = CE->getCallee()->IgnoreImpCasts();
+ continue;
----------------
The constness of the callee doesn't seem relevant here; should probably just `break` at this point.
================
Comment at: lib/Sema/SemaExpr.cpp:8594
@@ +8593,3 @@
+ if (const ValueDecl *VD = DRE->getDecl()) {
+ if (VD->getType().isConstQualified()) {
+ if (!DiagnosticEmitted) {
----------------
It'd be nice to also handle reference-to-const here.
================
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)
----------------
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`.
http://reviews.llvm.org/D4479
More information about the cfe-commits
mailing list