-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Reland: [clang] preserve class type sugar when taking pointer to member #132401
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
Conversation
Original PR: #130537 Originally reverted due to revert of dependent commit. Relanding with no changes. This changes the MemberPointerType representation to use a NestedNameSpecifier instead of a Type to represent the base class. Since the qualifiers are always parsed as nested names, there was an impedance mismatch when converting these back and forth into types, and this led to issues in preserving sugar. The nested names are indeed a better match for these, as the differences which a QualType can represent cannot be expressed syntatically, and they represent the use case more exactly, being either dependent or referring to a CXXRecord, unqualified. This patch also makes the MemberPointerType able to represent sugar for a {up/downcast}cast conversion of the base class, although for now the underlying type is canonical, as preserving the sugar up to that point requires further work. As usual, includes a few drive-by fixes in order to make use of the improvements.
@llvm/pr-subscribers-clang-static-analyzer-1 @llvm/pr-subscribers-lldb Author: Matheus Izvekov (mizvekov) ChangesOriginal PR: #130537 This changes the MemberPointerType representation to use a NestedNameSpecifier instead of a Type to represent the base class. Since the qualifiers are always parsed as nested names, there was an impedance mismatch when converting these back and forth into types, and this led to issues in preserving sugar. The nested names are indeed a better match for these, as the differences which a QualType can represent cannot be expressed syntatically, and they represent the use case more exactly, being either dependent or referring to a CXXRecord, unqualified. This patch also makes the MemberPointerType able to represent sugar for a {up/downcast}cast conversion of the base class, although for now the underlying type is canonical, as preserving the sugar up to that point requires further work. As usual, includes a few drive-by fixes in order to make use of the improvements. Patch is 143.32 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/132401.diff 71 Files Affected:
diff --git a/clang-tools-extra/clang-tidy/modernize/UseNullptrCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseNullptrCheck.cpp
index 108717e151b57..a6b00be75abf8 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseNullptrCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseNullptrCheck.cpp
@@ -493,8 +493,7 @@ UseNullptrCheck::UseNullptrCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
NullMacrosStr(Options.get("NullMacros", "NULL")),
IgnoredTypes(utils::options::parseStringList(Options.get(
- "IgnoredTypes",
- "std::_CmpUnspecifiedParam::;^std::__cmp_cat::__unspec"))) {
+ "IgnoredTypes", "_CmpUnspecifiedParam;^std::__cmp_cat::__unspec"))) {
StringRef(NullMacrosStr).split(NullMacros, ",");
}
diff --git a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
index b66cc8512fad6..bc49fa856bafc 100644
--- a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
+++ b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
@@ -178,7 +178,9 @@ bool isFunctionPointerConvertible(QualType From, QualType To) {
// Note: converting Derived::* to Base::* is a different kind of conversion,
// called Pointer-to-member conversion.
- return FromMember->getClass() == ToMember->getClass() &&
+ return FromMember->getQualifier() == ToMember->getQualifier() &&
+ FromMember->getMostRecentCXXRecordDecl() ==
+ ToMember->getMostRecentCXXRecordDecl() &&
FromMember->getPointeeType() == ToMember->getPointeeType();
}
diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
index fc54f89f4941e..602f61d9ecb41 100644
--- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
+++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
@@ -1489,7 +1489,7 @@ TEST_F(FindExplicitReferencesTest, AllRefsInFoo) {
"4: targets = {a}\n"
"5: targets = {a::b}, qualifier = 'a::'\n"
"6: targets = {a::b::S}\n"
- "7: targets = {a::b::S::type}, qualifier = 'struct S::'\n"
+ "7: targets = {a::b::S::type}, qualifier = 'S::'\n"
"8: targets = {y}, decl\n"},
{R"cpp(
void foo() {
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 159991e8db981..724cb62f1b0e6 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -267,6 +267,7 @@ Improvements to Clang's diagnostics
under the subgroup ``-Wunsafe-buffer-usage-in-libc-call``.
- Diagnostics on chained comparisons (``a < b < c``) are now an error by default. This can be disabled with
``-Wno-error=parentheses``.
+- Clang now better preserves the sugared types of pointers to member.
- The ``-Wshift-bool`` warning has been added to warn about shifting a boolean. (#GH28334)
- Fixed diagnostics adding a trailing ``::`` when printing some source code
constructs, like base classes.
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index f9a12260a6590..af8c49e99a7ce 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -1558,10 +1558,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
QualType getRValueReferenceType(QualType T) const;
/// Return the uniqued reference to the type for a member pointer to
- /// the specified type in the specified class.
- ///
- /// The class \p Cls is a \c Type because it could be a dependent name.
- QualType getMemberPointerType(QualType T, const Type *Cls) const;
+ /// the specified type in the specified nested name.
+ QualType getMemberPointerType(QualType T, NestedNameSpecifier *Qualifier,
+ const CXXRecordDecl *Cls) const;
/// Return a non-unique reference to the type for a variable array of
/// the specified element type.
diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h
index 3bc0bdff2bdd1..f557555e96e59 100644
--- a/clang/include/clang/AST/ASTNodeTraverser.h
+++ b/clang/include/clang/AST/ASTNodeTraverser.h
@@ -393,7 +393,9 @@ class ASTNodeTraverser
Visit(T->getPointeeType());
}
void VisitMemberPointerType(const MemberPointerType *T) {
- Visit(T->getClass());
+ // FIXME: Provide a NestedNameSpecifier visitor.
+ Visit(T->getQualifier()->getAsType());
+ Visit(T->getMostRecentCXXRecordDecl());
Visit(T->getPointeeType());
}
void VisitArrayType(const ArrayType *T) { Visit(T->getElementType()); }
@@ -485,7 +487,8 @@ class ASTNodeTraverser
}
}
void VisitMemberPointerTypeLoc(MemberPointerTypeLoc TL) {
- Visit(TL.getClassTInfo()->getTypeLoc());
+ // FIXME: Provide NestedNamespecifierLoc visitor.
+ Visit(TL.getQualifierLoc().getTypeLoc());
}
void VisitVariableArrayTypeLoc(VariableArrayTypeLoc TL) {
Visit(TL.getSizeExpr());
diff --git a/clang/include/clang/AST/CanonicalType.h b/clang/include/clang/AST/CanonicalType.h
index 50d1ba1b8f63f..35db68971e029 100644
--- a/clang/include/clang/AST/CanonicalType.h
+++ b/clang/include/clang/AST/CanonicalType.h
@@ -453,7 +453,7 @@ template<>
struct CanProxyAdaptor<MemberPointerType>
: public CanProxyBase<MemberPointerType> {
LLVM_CLANG_CANPROXY_TYPE_ACCESSOR(getPointeeType)
- LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(const Type *, getClass)
+ LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(NestedNameSpecifier *, getQualifier)
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(const CXXRecordDecl *,
getMostRecentCXXRecordDecl)
};
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 87a6c22b35ee8..e93d1d8eab56f 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1004,7 +1004,8 @@ DEF_TRAVERSE_TYPE(RValueReferenceType,
{ TRY_TO(TraverseType(T->getPointeeType())); })
DEF_TRAVERSE_TYPE(MemberPointerType, {
- TRY_TO(TraverseType(QualType(T->getClass(), 0)));
+ TRY_TO(TraverseNestedNameSpecifier(T->getQualifier()));
+ TRY_TO(TraverseDecl(T->getMostRecentCXXRecordDecl()));
TRY_TO(TraverseType(T->getPointeeType()));
})
@@ -1269,10 +1270,10 @@ DEF_TRAVERSE_TYPELOC(RValueReferenceType,
// We traverse this in the type case as well, but how is it not reached through
// the pointee type?
DEF_TRAVERSE_TYPELOC(MemberPointerType, {
- if (auto *TSI = TL.getClassTInfo())
- TRY_TO(TraverseTypeLoc(TSI->getTypeLoc()));
+ if (NestedNameSpecifierLoc QL = TL.getQualifierLoc())
+ TRY_TO(TraverseNestedNameSpecifierLoc(QL));
else
- TRY_TO(TraverseType(QualType(TL.getTypePtr()->getClass(), 0)));
+ TRY_TO(TraverseNestedNameSpecifier(TL.getTypePtr()->getQualifier()));
TRY_TO(TraverseTypeLoc(TL.getPointeeLoc()));
})
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 3c942f2ed7486..65756203f2073 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -3527,14 +3527,16 @@ class MemberPointerType : public Type, public llvm::FoldingSetNode {
QualType PointeeType;
/// The class of which the pointee is a member. Must ultimately be a
- /// RecordType, but could be a typedef or a template parameter too.
- const Type *Class;
+ /// CXXRecordType, but could be a typedef or a template parameter too.
+ NestedNameSpecifier *Qualifier;
- MemberPointerType(QualType Pointee, const Type *Cls, QualType CanonicalPtr)
+ MemberPointerType(QualType Pointee, NestedNameSpecifier *Qualifier,
+ QualType CanonicalPtr)
: Type(MemberPointer, CanonicalPtr,
- (Cls->getDependence() & ~TypeDependence::VariablyModified) |
+ (toTypeDependence(Qualifier->getDependence()) &
+ ~TypeDependence::VariablyModified) |
Pointee->getDependence()),
- PointeeType(Pointee), Class(Cls) {}
+ PointeeType(Pointee), Qualifier(Qualifier) {}
public:
QualType getPointeeType() const { return PointeeType; }
@@ -3551,21 +3553,21 @@ class MemberPointerType : public Type, public llvm::FoldingSetNode {
return !PointeeType->isFunctionProtoType();
}
- const Type *getClass() const { return Class; }
+ NestedNameSpecifier *getQualifier() const { return Qualifier; }
CXXRecordDecl *getMostRecentCXXRecordDecl() const;
- bool isSugared() const { return false; }
- QualType desugar() const { return QualType(this, 0); }
+ bool isSugared() const;
+ QualType desugar() const {
+ return isSugared() ? getCanonicalTypeInternal() : QualType(this, 0);
+ }
void Profile(llvm::FoldingSetNodeID &ID) {
- Profile(ID, getPointeeType(), getClass());
+ Profile(ID, getPointeeType(), getQualifier(), getMostRecentCXXRecordDecl());
}
static void Profile(llvm::FoldingSetNodeID &ID, QualType Pointee,
- const Type *Class) {
- ID.AddPointer(Pointee.getAsOpaquePtr());
- ID.AddPointer(Class);
- }
+ const NestedNameSpecifier *Qualifier,
+ const CXXRecordDecl *Cls);
static bool classof(const Type *T) {
return T->getTypeClass() == MemberPointer;
diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h
index a55a38335ef6a..17ce09fa5da4f 100644
--- a/clang/include/clang/AST/TypeLoc.h
+++ b/clang/include/clang/AST/TypeLoc.h
@@ -139,6 +139,7 @@ class TypeLoc {
}
/// Get the pointer where source information is stored.
+ // FIXME: This should provide a type-safe interface.
void *getOpaqueData() const {
return Data;
}
@@ -1355,7 +1356,7 @@ class BlockPointerTypeLoc : public PointerLikeTypeLoc<BlockPointerTypeLoc,
};
struct MemberPointerLocInfo : public PointerLikeLocInfo {
- TypeSourceInfo *ClassTInfo;
+ void *QualifierData = nullptr;
};
/// Wrapper for source info for member pointers.
@@ -1371,28 +1372,32 @@ class MemberPointerTypeLoc : public PointerLikeTypeLoc<MemberPointerTypeLoc,
setSigilLoc(Loc);
}
- const Type *getClass() const {
- return getTypePtr()->getClass();
- }
-
- TypeSourceInfo *getClassTInfo() const {
- return getLocalData()->ClassTInfo;
+ NestedNameSpecifierLoc getQualifierLoc() const {
+ return NestedNameSpecifierLoc(getTypePtr()->getQualifier(),
+ getLocalData()->QualifierData);
}
- void setClassTInfo(TypeSourceInfo* TI) {
- getLocalData()->ClassTInfo = TI;
+ void setQualifierLoc(NestedNameSpecifierLoc QualifierLoc) {
+ assert(QualifierLoc.getNestedNameSpecifier() ==
+ getTypePtr()->getQualifier() &&
+ "Inconsistent nested-name-specifier pointer");
+ getLocalData()->QualifierData = QualifierLoc.getOpaqueData();
}
void initializeLocal(ASTContext &Context, SourceLocation Loc) {
setSigilLoc(Loc);
- setClassTInfo(nullptr);
+ if (auto *Qualifier = getTypePtr()->getQualifier()) {
+ NestedNameSpecifierLocBuilder Builder;
+ Builder.MakeTrivial(Context, Qualifier, Loc);
+ setQualifierLoc(Builder.getWithLocInContext(Context));
+ } else
+ getLocalData()->QualifierData = nullptr;
}
SourceRange getLocalSourceRange() const {
- if (TypeSourceInfo *TI = getClassTInfo())
- return SourceRange(TI->getTypeLoc().getBeginLoc(), getStarLoc());
- else
- return SourceRange(getStarLoc());
+ if (NestedNameSpecifierLoc QL = getQualifierLoc())
+ return SourceRange(QL.getBeginLoc(), getStarLoc());
+ return SourceRange(getStarLoc());
}
};
diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td
index 6f1a76bd18fb5..27f71bf5cc62f 100644
--- a/clang/include/clang/AST/TypeProperties.td
+++ b/clang/include/clang/AST/TypeProperties.td
@@ -100,12 +100,15 @@ let Class = MemberPointerType in {
def : Property<"pointeeType", QualType> {
let Read = [{ node->getPointeeType() }];
}
- def : Property<"baseType", QualType> {
- let Read = [{ QualType(node->getClass(), 0) }];
+ def : Property<"Qualifier", NestedNameSpecifier> {
+ let Read = [{ node->getQualifier() }];
+ }
+ def : Property<"Cls", DeclRef> {
+ let Read = [{ node->getMostRecentCXXRecordDecl() }];
}
def : Creator<[{
- return ctx.getMemberPointerType(pointeeType, baseType.getTypePtr());
+ return ctx.getMemberPointerType(pointeeType, Qualifier, cast_or_null<CXXRecordDecl>(Cls));
}]>;
}
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index eed06657536f4..5fc5b0f57c8b5 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6975,10 +6975,8 @@ def err_illegal_decl_mempointer_to_reference : Error<
"'%0' declared as a member pointer to a reference of type %1">;
def err_illegal_decl_mempointer_to_void : Error<
"'%0' declared as a member pointer to void">;
-def err_illegal_decl_mempointer_in_nonclass : Error<
- "'%0' does not point into a class">;
-def err_mempointer_in_nonclass_type : Error<
- "member pointer refers into non-class type %0">;
+def err_illegal_decl_mempointer_in_nonclass
+ : Error<"'%0' does not point into a class">;
def err_reference_to_void : Error<"cannot form a reference to 'void'">;
def err_nonfunction_block_type : Error<
"block pointer to non-function type is invalid">;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 9724f0def743a..e215f07e2bf0a 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1348,6 +1348,12 @@ class Sema final : public SemaBase {
unsigned DiagID, bool ForceCheck = false,
bool ForceUnprivileged = false);
+ AccessResult CheckBaseClassAccess(
+ SourceLocation AccessLoc, CXXRecordDecl *Base, CXXRecordDecl *Derived,
+ const CXXBasePath &Path, unsigned DiagID,
+ llvm::function_ref<void(PartialDiagnostic &PD)> SetupPDiag,
+ bool ForceCheck = false, bool ForceUnprivileged = false);
+
/// Checks access to all the declarations in the given result set.
void CheckLookupAccess(const LookupResult &R);
@@ -14879,8 +14885,9 @@ class Sema final : public SemaBase {
///
/// \returns a member pointer type, if successful, or a NULL type if there was
/// an error.
- QualType BuildMemberPointerType(QualType T, QualType Class,
- SourceLocation Loc, DeclarationName Entity);
+ QualType BuildMemberPointerType(QualType T, NestedNameSpecifier *Qualifier,
+ CXXRecordDecl *Cls, SourceLocation Loc,
+ DeclarationName Entity);
/// Build a block pointer type.
///
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 68a02f3bbe1ec..de868ac821745 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -3322,7 +3322,8 @@ static void encodeTypeForFunctionPointerAuth(const ASTContext &Ctx,
case Type::MemberPointer: {
OS << "M";
const auto *MPT = T->castAs<MemberPointerType>();
- encodeTypeForFunctionPointerAuth(Ctx, OS, QualType(MPT->getClass(), 0));
+ encodeTypeForFunctionPointerAuth(
+ Ctx, OS, QualType(MPT->getQualifier()->getAsType(), 0));
encodeTypeForFunctionPointerAuth(Ctx, OS, MPT->getPointeeType());
return;
}
@@ -3511,7 +3512,8 @@ uint16_t ASTContext::getPointerAuthTypeDiscriminator(QualType T) {
if (PointeeType->castAs<FunctionProtoType>()->getExceptionSpecType() !=
EST_None) {
QualType FT = getFunctionTypeWithExceptionSpec(PointeeType, EST_None);
- T = getMemberPointerType(FT, MPT->getClass());
+ T = getMemberPointerType(FT, MPT->getQualifier(),
+ MPT->getMostRecentCXXRecordDecl());
}
}
std::unique_ptr<MangleContext> MC(createMangleContext());
@@ -4025,32 +4027,50 @@ QualType ASTContext::getRValueReferenceType(QualType T) const {
return QualType(New, 0);
}
-/// getMemberPointerType - Return the uniqued reference to the type for a
-/// member pointer to the specified type, in the specified class.
-QualType ASTContext::getMemberPointerType(QualType T, const Type *Cls) const {
+QualType ASTContext::getMemberPointerType(QualType T,
+ NestedNameSpecifier *Qualifier,
+ const CXXRecordDecl *Cls) const {
+ if (!Qualifier) {
+ assert(Cls && "At least one of Qualifier or Cls must be provided");
+ Qualifier = NestedNameSpecifier::Create(*this, /*Prefix=*/nullptr,
+ /*Template=*/false,
+ getTypeDeclType(Cls).getTypePtr());
+ } else if (!Cls) {
+ Cls = Qualifier->getAsRecordDecl();
+ }
// Unique pointers, to guarantee there is only one pointer of a particular
// structure.
llvm::FoldingSetNodeID ID;
- MemberPointerType::Profile(ID, T, Cls);
+ MemberPointerType::Profile(ID, T, Qualifier, Cls);
void *InsertPos = nullptr;
if (MemberPointerType *PT =
MemberPointerTypes.FindNodeOrInsertPos(ID, InsertPos))
return QualType(PT, 0);
+ NestedNameSpecifier *CanonicalQualifier = [&] {
+ if (!Cls)
+ return getCanonicalNestedNameSpecifier(Qualifier);
+ NestedNameSpecifier *R = NestedNameSpecifier::Create(
+ *this, /*Prefix=*/nullptr, /*Template=*/false,
+ Cls->getCanonicalDecl()->getTypeForDecl());
+ assert(R == getCanonicalNestedNameSpecifier(R));
+ return R;
+ }();
// If the pointee or class type isn't canonical, this won't be a canonical
// type either, so fill in the canonical type field.
QualType Canonical;
- if (!T.isCanonical() || !Cls->isCanonicalUnqualified()) {
- Canonical = getMemberPointerType(getCanonicalType(T),getCanonicalType(Cls));
-
+ if (!T.isCanonical() || Qualifier != CanonicalQualifier) {
+ Canonical =
+ getMemberPointerType(getCanonicalType(T), CanonicalQualifier, Cls);
+ assert(!cast<MemberPointerType>(Canonical)->isSugared());
// Get the new insert position for the node we care about.
- MemberPointerType *NewIP =
- MemberPointerTypes.FindNodeOrInsertPos(ID, InsertPos);
- assert(!NewIP && "Shouldn't be in the map!"); (void)NewIP;
+ [[maybe_unused]] MemberPointerType *NewIP =
+ MemberPointerTypes.FindNodeOrInsertPos(ID, InsertPos);
+ assert(!NewIP && "Shouldn't be in the map!");
}
auto *New = new (*this, alignof(MemberPointerType))
- MemberPointerType(T, Cls, Canonical);
+ MemberPointerType(T, Qualifier, Canonical);
Types.push_back(New);
MemberPointerTypes.InsertNode(New, InsertPos);
return QualType(New, 0);
@@ -6812,11 +6832,16 @@ bool ASTContext::UnwrapSimilarTypes(QualType &T1, QualType &T2,
return true;
}
- const auto *T1MPType = T1->getAs<MemberPointerType>();
- const auto *T2MPType = T2->getAs<MemberPointerType>();
- if (T1MPType && T2MPType &&
- hasSameUnqualifiedType(QualType(T1MPType->getClass(), 0),
- QualType(T2MPType->getClass(), 0))) {
+ if (const auto *T1MPType = T1->getAs<MemberPointerType>(),
+ *T2MPType = T2->getAs<MemberPointerType>();
+ T1MPType && T2MPType) {
+ if (auto *RD1 = T1MPType->getMostRecentCXXRecordDecl(),
+ *RD2 = T2MPType->getMostRecentCXXRecordDecl();
+ RD1 != RD2 && RD1->getCanonicalDecl() != RD2->getCanonicalDecl())
+ return false;
+ if (getCanonicalNestedNameSpecifier(T1MPType->getQualifier()) !=
+ getCanonicalNestedNameSpecifier(T2MPType->getQualifier()))
+ return false;
T1 = T1MPType->getPointeeType();
T2 = T2MPType->getPointeeType();
return true;
@@ -13857,11 +13882,12 @@ static QualType getCommonNonSugarTypeNode(ASTContext &Ctx, const Type *X,
case Type::MemberPointer: {
const auto *PX = cast<MemberPointerType>(X),
*PY = cast<MemberPointerType>(Y);
+ assert(declaresSameEntity(PX->getMostRecentCXXRecordDecl(),
+ PY->getMostRecentCXXRecordDecl()));
return Ctx.getMemberPointerType(
getCommonPointeeType(Ctx, PX, PY),
- Ctx.getCommonSugaredType(QualType(PX->getClass(), 0),
- ...
[truncated]
|
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.
LLDB changes LGTM
This change broke building Qt (tested with 6.8), ending up with errors like this:
This issue also persists on the latest git main as of right now. I've reduced the issue down to the following small reproducer: template <typename Func> struct CallableHelper {
static auto Resolve() -> Func;
};
struct QIODevice {
void d_func() { d_ptr; }
int d_ptr;
};
struct Callable : CallableHelper<void (QIODevice::*)()> {}; Reproducible with e.g. |
For that visitor, it is not expected that a type can traverse into a declaration. This makes the MemberPointer visitor conform to that rule. This turns the base class visitor into a CXXRecordType visitor, and only performs that visit in case it points to something different than the qualifier does. Fixes a regression introduced in #132401 As this fixes a regression which has not been released, there are no release notes.
For that visitor, it is not expected that a type can traverse into a declaration. This makes the MemberPointer visitor conform to that rule. This turns the base class visitor into a CXXRecordType visitor, and only performs that visit in case it points to something different than the qualifier does. Fixes a regression reported here: #132401 (comment) As this fixes a regression which has not been released, there are no release notes.
Thanks, for the report, will be fixed by #132551 |
Thanks for the quick fix, I can confirm that Qt builds fine for me again! |
Still seeing a crash even with the latest fix applied
In the meantime, I'll try reducing this |
Thanks, that would be appreciated. |
Sorry for not getting back sooner; had to let cvise run overnight. Repro w/ template <typename>
struct RunCallImpl;
template <typename Derived>
struct RunCallImpl<int (Derived::Info::*)(Derived *)> {};
template <typename d>
void RunCall(d) {
RunCallImpl<d>();
}
struct Filter {
virtual void MakeCall();
virtual ~Filter() = default;
};
template <typename Derived>
struct ImplementFilter : Filter {
void MakeCall() { RunCall(&Derived::Info::OnStuffHandler); }
};
struct FoobarFilter : ImplementFilter<FoobarFilter> {
struct Info {
int OnStuffHandler(FoobarFilter *);
};
}; Live link: https://godbolt.org/z/oboYrKM8G |
I don't see anything obvious. The only parts of the patch which touch anything similar to pointer-keyed containers are the type properties cache changes, like the linkage computation. |
This fixes a problem originally reported here: #132401 (comment) This makes sure we only serialize the class declaration when its strictly needed, and canonicalizes it, so it doesn't change in ways that don't round trip. There are no release notes, since this regression was never released.
(EDITED!) Memory Sanitizer complains about initialized value here - IsInvalid in TranslateSourceLocation Will try the patch now. |
@mizvekov - no, the patch doesn't help, or I did something wrong. |
Okay, if the problem is an uninitialized source location somewhere, then that patch doesn't help at all. |
Do you have a backtrace of that uninitialized read? |
Well, to be honest, I'm not completely sure these are the same problems, these are just sanitizer finding for the same compilation. Here is the MSan finding. Please note my source is somewhat behind the head, so locations might be off a bit |
Thanks for that stack trace, could be unrelated to this, but that still helped find an issue: #135450 |
Patched #135450 but still see the same MSan finding on my compilation - uninitialized value while checking location validity in Sorry but I'm not yet sure how to strip the reproducer from internal code so cannot provide it :| |
Using library with assertions, I'm seeing out-of-bounds source location read called from |
One thing that looks fishy, but that is even a different node, is this early return on if (ArgPack.getKind() != TemplateArgument::Pack)
return; This looks impossible to hit, because Can you turn that into an assert and try again? assert(ArgPack.getKind() == TemplateArgument::Pack); |
Didn't fire so far. Though this is non-deterministic, I might be (un)lucky. |
Comparing bcanalyzer --dump outputs for non-deterministic pcms, I see a lot of op values that differ by 1. I wonder if this might be something like the mismatch of Read Write UnsignedOrNone vs unsigned ... |
Could it be you are hitting an overflow/wrap around perhaps? Some of these nodes store the unsignedOrNone representation in a bitfield, but that's still 15 bits. |
In one of these changes we did bump one of these bit fields down to 15 bits, starting from 16. |
No, the problem seems to be in serialization/deserialization, there must be some mismatch between More precisely, I'm seeing that |
Hello, the minimized reproducer for the determinism issue is there: https://pastebin.com/6aL6rmBe . To build it, unpack it into separate files (via |
@emaxx-google thanks for the reproducer. I will be off to C++Now soon, so it's unlikely I will have time to take a look at |
We started seeing the following issue, and bisected to this commit.
Here's a small test case:
If we just compile with this commit, we ran into this issue and it is blocking us to roll Clang in our project. |
This commit fixes the nondeterminism issue in C++ header module enabled builds which were observed after #132401. The issue was related to the fact that the hash set operation in MemberPointerType::Profile() was triggering getMostRecentDecl(). As the latter may trigger the loading of new entities from the external AST source, this was presumably causing reentrant modification of data structure or some other issue that affects compiler's output in a nondeterministic way (likely depending on specific values hashes/pointers have). The change should otherwise be a no-op, because whether we take a "most recent" or "any" Decl shouldn't matter since `getCanonicalDecl()` is called on it anyway inside `MemberPointerType::Profile()`. We haven't been able to come up with a deterministic regression test for this fix.
This commit fixes the nondeterminism issue in C++ header module enabled builds which were observed after llvm#132401. The issue was related to the fact that the hash set operation in MemberPointerType::Profile() was triggering getMostRecentDecl(). As the latter may trigger the loading of new entities from the external AST source, this was presumably causing reentrant modification of data structure or some other issue that affects compiler's output in a nondeterministic way (likely depending on specific values hashes/pointers have). The change should otherwise be a no-op, because whether we take a "most recent" or "any" Decl shouldn't matter since `getCanonicalDecl()` is called on it anyway inside `MemberPointerType::Profile()`. We haven't been able to come up with a deterministic regression test for this fix.
This commit fixes the nondeterminism issue in C++ header module enabled builds which were observed after llvm#132401. The issue was related to the fact that the hash set operation in MemberPointerType::Profile() was triggering getMostRecentDecl(). As the latter may trigger the loading of new entities from the external AST source, this was presumably causing reentrant modification of data structure or some other issue that affects compiler's output in a nondeterministic way (likely depending on specific values hashes/pointers have). The change should otherwise be a no-op, because whether we take a "most recent" or "any" Decl shouldn't matter since `getCanonicalDecl()` is called on it anyway inside `MemberPointerType::Profile()`. We haven't been able to come up with a deterministic regression test for this fix.
This commit fixes the nondeterminism issue in C++ header module enabled builds which were observed after llvm#132401. The issue was related to the fact that the hash set operation in MemberPointerType::Profile() was triggering getMostRecentDecl(). As the latter may trigger the loading of new entities from the external AST source, this was presumably causing reentrant modification of data structure or some other issue that affects compiler's output in a nondeterministic way (likely depending on specific values hashes/pointers have). The change should otherwise be a no-op, because whether we take a "most recent" or "any" Decl shouldn't matter since `getCanonicalDecl()` is called on it anyway inside `MemberPointerType::Profile()`. We haven't been able to come up with a deterministic regression test for this fix.
This commit fixes the nondeterminism issue in C++ header module enabled builds which were observed after llvm/llvm-project#132401. The issue was related to the fact that the hash set operation in MemberPointerType::Profile() was triggering getMostRecentDecl(). As the latter may trigger the loading of new entities from the external AST source, this was presumably causing reentrant modification of data structure or some other issue that affects compiler's output in a nondeterministic way (likely depending on specific values hashes/pointers have). The change should otherwise be a no-op, because whether we take a "most recent" or "any" Decl shouldn't matter since `getCanonicalDecl()` is called on it anyway inside `MemberPointerType::Profile()`. We haven't been able to come up with a deterministic regression test for this fix.
This commit fixes the nondeterminism issue in C++ header module enabled builds which were observed after llvm#132401. The issue was related to the fact that the hash set operation in MemberPointerType::Profile() was triggering getMostRecentDecl(). As the latter may trigger the loading of new entities from the external AST source, this was presumably causing reentrant modification of data structure or some other issue that affects compiler's output in a nondeterministic way (likely depending on specific values hashes/pointers have). The change should otherwise be a no-op, because whether we take a "most recent" or "any" Decl shouldn't matter since `getCanonicalDecl()` is called on it anyway inside `MemberPointerType::Profile()`. We haven't been able to come up with a deterministic regression test for this fix.
Original PR: #130537
Originally reverted due to revert of dependent commit. Relanding with no changes.
This changes the MemberPointerType representation to use a NestedNameSpecifier instead of a Type to represent the base class.
Since the qualifiers are always parsed as nested names, there was an impedance mismatch when converting these back and forth into types, and this led to issues in preserving sugar.
The nested names are indeed a better match for these, as the differences which a QualType can represent cannot be expressed syntatically, and they represent the use case more exactly, being either dependent or referring to a CXXRecord, unqualified.
This patch also makes the MemberPointerType able to represent sugar for a {up/downcast}cast conversion of the base class, although for now the underlying type is canonical, as preserving the sugar up to that point requires further work.
As usual, includes a few drive-by fixes in order to make use of the improvements.