-
Notifications
You must be signed in to change notification settings - Fork 11.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Clang][Sema] Fix crash when diagnosing near-match for 'constexpr' redeclaration in C++11 #92452
Conversation
@llvm/pr-subscribers-clang Author: Krystian Stasiowski (sdkrystian) ChangesClang crashes when diagnosing the following invalid redeclaration in C++11: struct A {
void f();
};
constexpr void A::f() { } // crash here This happens because This patch changes the accessors for cv-qualifier-seq Full diff: https://github.com/llvm/llvm-project/pull/92452.diff 4 Files Affected:
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 2d2928e418623..411a5f752899d 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -724,6 +724,8 @@ Bug Fixes to C++ Support
templates during partial ordering when deducing template arguments from a function declaration or when
taking the address of a function template.
- Fix a bug with checking constrained non-type template parameters for equivalence. Fixes (#GH77377).
+- Fix a C++11 crash when a non-const non-static member function is defined out-of-line with
+ the ``constexpr`` specifier.
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 23bc780e04979..44d96db54b5f0 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -1527,20 +1527,20 @@ struct DeclaratorChunk {
/// Retrieve the location of the 'const' qualifier.
SourceLocation getConstQualifierLoc() const {
- assert(MethodQualifiers);
- return MethodQualifiers->getConstSpecLoc();
+ return MethodQualifiers ? MethodQualifiers->getConstSpecLoc()
+ : SourceLocation();
}
/// Retrieve the location of the 'volatile' qualifier.
SourceLocation getVolatileQualifierLoc() const {
- assert(MethodQualifiers);
- return MethodQualifiers->getVolatileSpecLoc();
+ return MethodQualifiers ? MethodQualifiers->getVolatileSpecLoc()
+ : SourceLocation();
}
/// Retrieve the location of the 'restrict' qualifier.
SourceLocation getRestrictQualifierLoc() const {
- assert(MethodQualifiers);
- return MethodQualifiers->getRestrictSpecLoc();
+ return MethodQualifiers ? MethodQualifiers->getRestrictSpecLoc()
+ : SourceLocation();
}
/// Retrieve the location of the 'mutable' qualifier, if any.
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 0dbdf923df95a..22749dc4799bc 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -9203,15 +9203,15 @@ static NamedDecl *DiagnoseInvalidRedeclaration(
<< Idx << FDParam->getType()
<< NewFD->getParamDecl(Idx - 1)->getType();
} else if (FDisConst != NewFDisConst) {
- SemaRef.Diag(FD->getLocation(), diag::note_member_def_close_const_match)
- << NewFDisConst << FD->getSourceRange().getEnd()
- << (NewFDisConst
- ? FixItHint::CreateRemoval(ExtraArgs.D.getFunctionTypeInfo()
- .getConstQualifierLoc())
- : FixItHint::CreateInsertion(ExtraArgs.D.getFunctionTypeInfo()
- .getRParenLoc()
- .getLocWithOffset(1),
- " const"));
+ auto DB = SemaRef.Diag(FD->getLocation(),
+ diag::note_member_def_close_const_match)
+ << NewFDisConst << FD->getSourceRange().getEnd();
+ if (const auto &FTI = ExtraArgs.D.getFunctionTypeInfo(); !NewFDisConst)
+ DB << FixItHint::CreateInsertion(FTI.getRParenLoc().getLocWithOffset(1),
+ " const");
+ else if (SourceLocation ConstLoc = FTI.getConstQualifierLoc();
+ ConstLoc.isValid())
+ DB << FixItHint::CreateRemoval(ConstLoc);
} else
SemaRef.Diag(FD->getLocation(),
IsMember ? diag::note_member_def_close_match
diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp
index a28a5f91c4775..788e93b56bb38 100644
--- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp
+++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp
@@ -154,3 +154,14 @@ namespace {
// FIXME: We should diagnose this prior to C++17.
const int &r = A::n;
}
+
+#if __cplusplus < 201402L
+namespace ImplicitConstexprDef {
+ struct A {
+ void f(); // expected-note {{member declaration does not match because it is not const qualified}}
+ };
+
+ constexpr void A::f() { } // expected-warning {{'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const' to avoid a change in behavior}}
+ // expected-error@-1 {{out-of-line definition of 'f' does not match any declaration in 'ImplicitConstexprDef::A'}}
+}
+#endif
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AaronBallman is probably the best one to review DeclChunk stuff, perhaps @cor3ntin
@@ -724,6 +724,8 @@ Bug Fixes to C++ Support | |||
templates during partial ordering when deducing template arguments from a function declaration or when | |||
taking the address of a function template. | |||
- Fix a bug with checking constrained non-type template parameters for equivalence. Fixes (#GH77377). | |||
- Fix a C++11 crash when a non-const non-static member function is defined out-of-line with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a bug # for this one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't know, I just happened upon it when working on #92449... I'll check
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked, but couldn't find any
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nevermind, I did :) #61004
clang/include/clang/Sema/DeclSpec.h
Outdated
@@ -1527,20 +1527,20 @@ struct DeclaratorChunk { | |||
|
|||
/// Retrieve the location of the 'const' qualifier. | |||
SourceLocation getConstQualifierLoc() const { | |||
assert(MethodQualifiers); | |||
return MethodQualifiers->getConstSpecLoc(); | |||
return MethodQualifiers ? MethodQualifiers->getConstSpecLoc() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm... these asserts make sense in just about every situation except for the 'fixits'. I wonder if there is instead value in capturing that we did it with a 'fixit' instead? So the assert becomes MethodQualifiers || WasFixConstFixit
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DeclSpec.h
is basically used only to gather information from the parser, and so from that perspective, I think the assert
makes sense. If the caller is asking for the location of the const
qualifier and one wasn't written in source, the caller is doing something out of the ordinary.
I think the fix-it code should be guarded by a call to hasMethodTypeQualifiers()
instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AaronBallman Updated
auto DB = SemaRef.Diag(FD->getLocation(), | ||
diag::note_member_def_close_const_match) | ||
<< NewFDisConst << FD->getSourceRange().getEnd(); | ||
if (const auto &FTI = ExtraArgs.D.getFunctionTypeInfo(); !NewFDisConst) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (const auto &FTI = ExtraArgs.D.getFunctionTypeInfo(); !NewFDisConst) | |
if (const auto &FTI = ExtraArgs.D.getFunctionTypeInfo(); NewFDisConst) |
This is closer to what we mean, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Er, no. We want to suggest removing const
when NewFDisConst
is true
, or suggest inserting it if false
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Woops, I forgot my other change :D I meant FDisConst
since i saw the diagnostic was in terms of the 'old' value? But I could have been mistaken.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would work either way... since this is guarded by if (FDisConst != NewFDisConst)
, !NewFDisConst
implies FDisConst
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I agree it is not really a meaningful change, besides being a bit of a 'say what you mean'. That is, use the variable that is more closely related to what we MEAN. I haven't reviewed this in long enough to know if I was correct here, so if you're sure that the variable you're using is the one that properly states the intent, feel free to ignore hte comment.
clang/lib/Sema/SemaDecl.cpp
Outdated
if (const auto &FTI = ExtraArgs.D.getFunctionTypeInfo(); !NewFDisConst) | ||
DB << FixItHint::CreateInsertion(FTI.getRParenLoc().getLocWithOffset(1), | ||
" const"); | ||
else if (SourceLocation ConstLoc = FTI.getConstQualifierLoc(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is the only use of it, I wonder if we could just better detect this case...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I opted for the "return SourceLocation()
if no const
qualifier is present" approach because it doesn't require the caller to check hasMethodTypeQualifiers()
prior to calling getConstQualifierLoc()
. Makes it less prone to being the cause of future crashes :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that hides subtle issues; the caller should really know the difference between "const qualifier as written" and "semantically const".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough :)
569e2f1
to
46fd950
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
d7895e4
to
e19f1d6
Compare
e19f1d6
to
95d1cb2
Compare
Clang crashes when diagnosing the following invalid redeclaration in C++11:
This happens because
DiagnoseInvalidRedeclaration
tries to create a fix-it to removeconst
from the out-of-line declaration off
, but there is noSourceLocation
for theconst
qualifier (it's implicitlyconst
due toconstexpr
) and an assert inFunctionTypeInfo::getConstQualifierLoc
fails.This patch changes the accessors for cv-qualifier-seq
SourceLocations
inFunctionTypeInfo
to return an invalidSourceLocation
if no cv-qualifier-seq is present, and changesDiagnoseInvalidRedeclaration
to only suggest the removal of theconst
qualifier when it was explicitly specified.