Skip to content

[clang] NFC: Unify implementations of CheckMemberPointerConversion #131966

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

Merged
merged 1 commit into from
Mar 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -10057,15 +10057,24 @@ class Sema final : public SemaBase {
bool InOverloadResolution,
QualType &ConvertedType);

enum class MemberPointerConversionResult {
Success,
DifferentPointee,
NotDerived,
Ambiguous,
Virtual,
Inaccessible
};
enum class MemberPointerConversionDirection : bool { Downcast, Upcast };
/// CheckMemberPointerConversion - Check the member pointer conversion from
/// the expression From to the type ToType. This routine checks for ambiguous
/// or virtual or inaccessible base-to-derived member pointer conversions for
/// which IsMemberPointerConversion has already returned true. It returns true
/// and produces a diagnostic if there was an error, or returns false
/// otherwise.
bool CheckMemberPointerConversion(Expr *From, QualType ToType, CastKind &Kind,
CXXCastPath &BasePath,
bool IgnoreBaseAccess);
/// which IsMemberPointerConversion has already returned true. It produces a
// diagnostic if there was an error.
MemberPointerConversionResult CheckMemberPointerConversion(
QualType FromType, const MemberPointerType *ToPtrType, CastKind &Kind,
CXXCastPath &BasePath, SourceLocation CheckLoc, SourceRange OpRange,
bool IgnoreBaseAccess, MemberPointerConversionDirection Direction);

/// IsQualificationConversion - Determines whether the conversion from
/// an rvalue of type FromType to ToType is a qualification conversion
Expand Down
27 changes: 12 additions & 15 deletions clang/lib/Sema/SemaAccess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1874,33 +1874,30 @@ Sema::AccessResult Sema::CheckAddressOfMemberAccess(Expr *OvlExpr,
}

Sema::AccessResult Sema::CheckBaseClassAccess(SourceLocation AccessLoc,
QualType Base,
QualType Derived,
QualType Base, QualType Derived,
const CXXBasePath &Path,
unsigned DiagID,
bool ForceCheck,
unsigned DiagID, bool ForceCheck,
bool ForceUnprivileged) {
if (!ForceCheck && !getLangOpts().AccessControl)
return AR_accessible;

if (Path.Access == AS_public)
return AR_accessible;

CXXRecordDecl *BaseD, *DerivedD;
BaseD = cast<CXXRecordDecl>(Base->castAs<RecordType>()->getDecl());
DerivedD = cast<CXXRecordDecl>(Derived->castAs<RecordType>()->getDecl());

AccessTarget Entity(Context, AccessTarget::Base, BaseD, DerivedD,
Path.Access);
AccessTarget Entity(Context, AccessTarget::Base, Base->getAsCXXRecordDecl(),
Derived->getAsCXXRecordDecl(), Path.Access);
if (DiagID)
Entity.setDiag(DiagID) << Derived << Base;

if (ForceUnprivileged) {
switch (CheckEffectiveAccess(*this, EffectiveContext(),
AccessLoc, Entity)) {
case ::AR_accessible: return Sema::AR_accessible;
case ::AR_inaccessible: return Sema::AR_inaccessible;
case ::AR_dependent: return Sema::AR_dependent;
switch (
CheckEffectiveAccess(*this, EffectiveContext(), AccessLoc, Entity)) {
case ::AR_accessible:
return Sema::AR_accessible;
case ::AR_inaccessible:
return Sema::AR_inaccessible;
case ::AR_dependent:
return Sema::AR_dependent;
}
llvm_unreachable("unexpected result from CheckEffectiveAccess");
}
Expand Down
78 changes: 14 additions & 64 deletions clang/lib/Sema/SemaCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1794,72 +1794,25 @@ TryStaticMemberPointerUpcast(Sema &Self, ExprResult &SrcExpr, QualType SrcType,
}
}

const MemberPointerType *SrcMemPtr = SrcType->getAs<MemberPointerType>();
if (!SrcMemPtr) {
msg = diag::err_bad_static_cast_member_pointer_nonmp;
return TC_NotApplicable;
}

// Lock down the inheritance model right now in MS ABI, whether or not the
// pointee types are the same.
if (Self.Context.getTargetInfo().getCXXABI().isMicrosoft()) {
(void)Self.isCompleteType(OpRange.getBegin(), SrcType);
(void)Self.isCompleteType(OpRange.getBegin(), DestType);
}

// T == T, modulo cv
if (!Self.Context.hasSameUnqualifiedType(SrcMemPtr->getPointeeType(),
DestMemPtr->getPointeeType()))
return TC_NotApplicable;

// B base of D
QualType SrcClass(SrcMemPtr->getClass(), 0);
QualType DestClass(DestMemPtr->getClass(), 0);
CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
/*DetectVirtual=*/true);
if (!Self.IsDerivedFrom(OpRange.getBegin(), SrcClass, DestClass, Paths))
switch (Self.CheckMemberPointerConversion(
SrcType, DestMemPtr, Kind, BasePath, OpRange.getBegin(), OpRange, CStyle,
Sema::MemberPointerConversionDirection::Upcast)) {
case Sema::MemberPointerConversionResult::Success:
if (Kind == CK_NullToMemberPointer) {
msg = diag::err_bad_static_cast_member_pointer_nonmp;
return TC_NotApplicable;
}
break;
case Sema::MemberPointerConversionResult::DifferentPointee:
case Sema::MemberPointerConversionResult::NotDerived:
return TC_NotApplicable;

// B is a base of D. But is it an allowed base? If not, it's a hard error.
if (Paths.isAmbiguous(Self.Context.getCanonicalType(DestClass))) {
Paths.clear();
Paths.setRecordingPaths(true);
bool StillOkay =
Self.IsDerivedFrom(OpRange.getBegin(), SrcClass, DestClass, Paths);
assert(StillOkay);
(void)StillOkay;
std::string PathDisplayStr = Self.getAmbiguousPathsDisplayString(Paths);
Self.Diag(OpRange.getBegin(), diag::err_ambiguous_memptr_conv)
<< 1 << SrcClass << DestClass << PathDisplayStr << OpRange;
msg = 0;
return TC_Failed;
}

if (const RecordType *VBase = Paths.getDetectedVirtual()) {
Self.Diag(OpRange.getBegin(), diag::err_memptr_conv_via_virtual)
<< SrcClass << DestClass << QualType(VBase, 0) << OpRange;
case Sema::MemberPointerConversionResult::Ambiguous:
case Sema::MemberPointerConversionResult::Virtual:
case Sema::MemberPointerConversionResult::Inaccessible:
msg = 0;
return TC_Failed;
}

if (!CStyle) {
switch (Self.CheckBaseClassAccess(OpRange.getBegin(),
DestClass, SrcClass,
Paths.front(),
diag::err_upcast_to_inaccessible_base)) {
case Sema::AR_accessible:
case Sema::AR_delayed:
case Sema::AR_dependent:
// Optimistically assume that the delayed and dependent cases
// will work out.
break;

case Sema::AR_inaccessible:
msg = 0;
return TC_Failed;
}
}

if (WasOverloadedFunction) {
// Resolve the address of the overloaded function again, this time
// allowing complaints if something goes wrong.
Expand All @@ -1878,9 +1831,6 @@ TryStaticMemberPointerUpcast(Sema &Self, ExprResult &SrcExpr, QualType SrcType,
return TC_Failed;
}
}

Self.BuildBasePathArray(Paths, BasePath);
Kind = CK_DerivedToBaseMemberPointer;
return TC_Success;
}

Expand Down
27 changes: 19 additions & 8 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4666,18 +4666,29 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
case ICK_Pointer_Member: {
CastKind Kind;
CXXCastPath BasePath;
if (CheckMemberPointerConversion(From, ToType, Kind, BasePath, CStyle))
switch (CheckMemberPointerConversion(
From->getType(), ToType->castAs<MemberPointerType>(), Kind, BasePath,
From->getExprLoc(), From->getSourceRange(), CStyle,
MemberPointerConversionDirection::Downcast)) {
case MemberPointerConversionResult::Success:
assert(Kind != CK_NullToMemberPointer ||
From->isNullPointerConstant(Context,
Expr::NPC_ValueDependentIsNull) &&
"Expr must be null pointer constant!");
break;
case MemberPointerConversionResult::Inaccessible:
break;
case MemberPointerConversionResult::DifferentPointee:
llvm_unreachable("unexpected result");
case MemberPointerConversionResult::NotDerived:
llvm_unreachable("Should not have been called if derivation isn't OK.");
case MemberPointerConversionResult::Ambiguous:
case MemberPointerConversionResult::Virtual:
return ExprError();
}
if (CheckExceptionSpecCompatibility(From, ToType))
return ExprError();

// We may not have been able to figure out what this member pointer resolved
// to up until this exact point. Attempt to lock-in it's inheritance model.
if (Context.getTargetInfo().getCXXABI().isMicrosoft()) {
(void)isCompleteType(From->getExprLoc(), From->getType());
(void)isCompleteType(From->getExprLoc(), ToType);
}

From =
ImpCastExprToType(From, ToType, Kind, VK_PRValue, &BasePath, CCK).get();
break;
Expand Down
95 changes: 55 additions & 40 deletions clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3548,64 +3548,79 @@ bool Sema::IsMemberPointerConversion(Expr *From, QualType FromType,
return false;
}

bool Sema::CheckMemberPointerConversion(Expr *From, QualType ToType,
CastKind &Kind,
CXXCastPath &BasePath,
bool IgnoreBaseAccess) {
QualType FromType = From->getType();
Sema::MemberPointerConversionResult Sema::CheckMemberPointerConversion(
QualType FromType, const MemberPointerType *ToPtrType, CastKind &Kind,
CXXCastPath &BasePath, SourceLocation CheckLoc, SourceRange OpRange,
bool IgnoreBaseAccess, MemberPointerConversionDirection Direction) {
const MemberPointerType *FromPtrType = FromType->getAs<MemberPointerType>();
if (!FromPtrType) {
// This must be a null pointer to member pointer conversion
assert(From->isNullPointerConstant(Context,
Expr::NPC_ValueDependentIsNull) &&
"Expr must be null pointer constant!");
Kind = CK_NullToMemberPointer;
return false;
return MemberPointerConversionResult::Success;
}

// Lock down the inheritance model right now in MS ABI, whether or not the
// pointee types are the same.
if (Context.getTargetInfo().getCXXABI().isMicrosoft()) {
(void)isCompleteType(CheckLoc, FromType);
(void)isCompleteType(CheckLoc, QualType(ToPtrType, 0));
}

const MemberPointerType *ToPtrType = ToType->getAs<MemberPointerType>();
assert(ToPtrType && "No member pointer cast has a target type "
"that is not a member pointer.");
// T == T, modulo cv
if (Direction == MemberPointerConversionDirection::Upcast &&
!Context.hasSameUnqualifiedType(FromPtrType->getPointeeType(),
ToPtrType->getPointeeType()))
return MemberPointerConversionResult::DifferentPointee;

QualType FromClass = QualType(FromPtrType->getClass(), 0);
QualType ToClass = QualType(ToPtrType->getClass(), 0);
QualType FromClass = QualType(FromPtrType->getClass(), 0),
ToClass = QualType(ToPtrType->getClass(), 0);

// FIXME: What about dependent types?
assert(FromClass->isRecordType() && "Pointer into non-class.");
assert(ToClass->isRecordType() && "Pointer into non-class.");
QualType Base = FromClass, Derived = ToClass;
if (Direction == MemberPointerConversionDirection::Upcast)
std::swap(Base, Derived);

CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
/*DetectVirtual=*/true);
bool DerivationOkay =
IsDerivedFrom(From->getBeginLoc(), ToClass, FromClass, Paths);
assert(DerivationOkay &&
"Should not have been called if derivation isn't OK.");
(void)DerivationOkay;

if (Paths.isAmbiguous(Context.getCanonicalType(FromClass).
getUnqualifiedType())) {
std::string PathDisplayStr = getAmbiguousPathsDisplayString(Paths);
Diag(From->getExprLoc(), diag::err_ambiguous_memptr_conv)
<< 0 << FromClass << ToClass << PathDisplayStr << From->getSourceRange();
return true;
if (!IsDerivedFrom(OpRange.getBegin(), Derived, Base, Paths))
return MemberPointerConversionResult::NotDerived;

if (Paths.isAmbiguous(Base->getCanonicalTypeUnqualified())) {
Diag(CheckLoc, diag::err_ambiguous_memptr_conv)
<< int(Direction) << FromClass << ToClass
<< getAmbiguousPathsDisplayString(Paths) << OpRange;
return MemberPointerConversionResult::Ambiguous;
}

if (const RecordType *VBase = Paths.getDetectedVirtual()) {
Diag(From->getExprLoc(), diag::err_memptr_conv_via_virtual)
<< FromClass << ToClass << QualType(VBase, 0)
<< From->getSourceRange();
return true;
Diag(CheckLoc, diag::err_memptr_conv_via_virtual)
<< FromClass << ToClass << QualType(VBase, 0) << OpRange;
return MemberPointerConversionResult::Virtual;
}

if (!IgnoreBaseAccess)
CheckBaseClassAccess(From->getExprLoc(), FromClass, ToClass,
Paths.front(),
diag::err_downcast_from_inaccessible_base);

// Must be a base to derived member conversion.
BuildBasePathArray(Paths, BasePath);
Kind = CK_BaseToDerivedMemberPointer;
return false;
Kind = Direction == MemberPointerConversionDirection::Upcast
? CK_DerivedToBaseMemberPointer
: CK_BaseToDerivedMemberPointer;

if (!IgnoreBaseAccess)
switch (CheckBaseClassAccess(
CheckLoc, Base, Derived, Paths.front(),
Direction == MemberPointerConversionDirection::Upcast
? diag::err_upcast_to_inaccessible_base
: diag::err_downcast_from_inaccessible_base)) {
case Sema::AR_accessible:
case Sema::AR_delayed:
case Sema::AR_dependent:
// Optimistically assume that the delayed and dependent cases
// will work out.
break;

case Sema::AR_inaccessible:
return MemberPointerConversionResult::Inaccessible;
}

return MemberPointerConversionResult::Success;
}

/// Determine whether the lifetime conversion between the two given
Expand Down
Loading