Skip to content

Commit ebdea9c

Browse files
committed
* Use overload on special members
* fix tests (class with a const member are not implicitly relocatable) * Factorize some code to only compute default-movable once
1 parent bc04765 commit ebdea9c

File tree

4 files changed

+202
-126
lines changed

4 files changed

+202
-126
lines changed

clang/include/clang/AST/DeclCXX.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,13 @@ class CXXRecordDecl : public RecordDecl {
760760
return data().DefaultedMoveAssignmentIsDeleted;
761761
}
762762

763+
bool defaultedCopyAssignmentIsDeleted() const {
764+
assert((!needsOverloadResolutionForCopyAssignment() ||
765+
(data().DeclaredSpecialMembers & SMF_CopyAssignment)) &&
766+
"this property has not yet been computed by Sema");
767+
return data().DefaultedCopyAssignmentIsDeleted;
768+
}
769+
763770
/// \c true if a defaulted destructor for this class would be deleted.
764771
bool defaultedDestructorIsDeleted() const {
765772
assert((!needsOverloadResolutionForDestructor() ||

clang/include/clang/Sema/Sema.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3995,9 +3995,7 @@ class Sema final : public SemaBase {
39953995
void ActOnTagFinishDefinition(Scope *S, Decl *TagDecl,
39963996
SourceRange BraceRange);
39973997

3998-
void CheckCXX2CTriviallyRelocatable(CXXRecordDecl *D);
3999-
4000-
void CheckCXX2CReplaceable(CXXRecordDecl *D);
3998+
void CheckCXX2CRelocatableAndReplaceable(CXXRecordDecl *D);
40013999

40024000
void ActOnTagFinishSkippedDefinition(SkippedDefinitionContext Context);
40034001

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 152 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -7221,8 +7221,7 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) {
72217221
checkClassLevelDLLAttribute(Record);
72227222
checkClassLevelCodeSegAttribute(Record);
72237223

7224-
CheckCXX2CTriviallyRelocatable(Record);
7225-
CheckCXX2CReplaceable(Record);
7224+
CheckCXX2CRelocatableAndReplaceable(Record);
72267225

72277226
bool ClangABICompat4 =
72287227
Context.getLangOpts().getClangABICompat() <= LangOptions::ClangABI::Ver4;
@@ -7261,82 +7260,116 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) {
72617260
}
72627261
}
72637262

7264-
static bool hasSuitableConstructorForRelocation(CXXRecordDecl *D,
7265-
bool AllowUserDefined) {
7266-
assert(D->hasDefinition() && !D->isInvalidDecl());
7263+
static CXXMethodDecl *
7264+
LookupSpecialMemberFromXValue(Sema &SemaRef, CXXRecordDecl *RD, bool Assign) {
7265+
RD = RD->getDefinition();
7266+
SourceLocation LookupLoc = RD->getLocation();
7267+
7268+
CanQualType CanTy = SemaRef.getASTContext().getCanonicalType(
7269+
SemaRef.getASTContext().getTagDeclType(RD));
7270+
DeclarationName Name;
7271+
Expr *Arg = nullptr;
7272+
unsigned NumArgs;
7273+
7274+
QualType ArgType = CanTy;
7275+
ExprValueKind VK = clang::VK_XValue;
7276+
7277+
if (Assign)
7278+
Name =
7279+
SemaRef.getASTContext().DeclarationNames.getCXXOperatorName(OO_Equal);
7280+
else
7281+
Name =
7282+
SemaRef.getASTContext().DeclarationNames.getCXXConstructorName(CanTy);
7283+
7284+
OpaqueValueExpr FakeArg(LookupLoc, ArgType, VK);
7285+
NumArgs = 1;
7286+
Arg = &FakeArg;
7287+
7288+
// Create the object argument
7289+
QualType ThisTy = CanTy;
7290+
Expr::Classification Classification =
7291+
OpaqueValueExpr(LookupLoc, ThisTy, VK_LValue)
7292+
.Classify(SemaRef.getASTContext());
7293+
7294+
// Now we perform lookup on the name we computed earlier and do overload
7295+
// resolution. Lookup is only performed directly into the class since there
7296+
// will always be a (possibly implicit) declaration to shadow any others.
7297+
OverloadCandidateSet OCS(LookupLoc, OverloadCandidateSet::CSK_Normal);
7298+
DeclContext::lookup_result R = RD->lookup(Name);
7299+
7300+
if (R.empty())
7301+
return nullptr;
72677302

7268-
bool HasDeletedMoveConstructor = false;
7269-
bool HasDeletedCopyConstructor = false;
7270-
bool HasMoveConstructor = D->needsImplicitMoveConstructor();
7271-
bool HasCopyConstructor = D->needsImplicitCopyConstructor();
7272-
bool HasDefaultedMoveConstructor = D->needsImplicitMoveConstructor();
7273-
bool HasDefaultedCopyConstructor = D->needsImplicitCopyConstructor();
7303+
// Copy the candidates as our processing of them may load new declarations
7304+
// from an external source and invalidate lookup_result.
7305+
SmallVector<NamedDecl *, 8> Candidates(R.begin(), R.end());
72747306

7275-
for (const Decl *D : D->decls()) {
7276-
auto *MD = dyn_cast<CXXConstructorDecl>(D);
7277-
if (!MD || MD->isIneligibleOrNotSelected())
7307+
for (NamedDecl *CandDecl : Candidates) {
7308+
if (CandDecl->isInvalidDecl())
72787309
continue;
72797310

7280-
if (MD->isMoveConstructor()) {
7281-
HasMoveConstructor = true;
7282-
if (MD->isDefaulted())
7283-
HasDefaultedMoveConstructor = true;
7284-
if (MD->isDeleted())
7285-
HasDeletedMoveConstructor = true;
7286-
} else if (MD->isCopyConstructor()) {
7287-
HasCopyConstructor = true;
7288-
if (MD->isDefaulted())
7289-
HasDefaultedCopyConstructor = true;
7290-
if (MD->isDeleted())
7291-
HasDeletedCopyConstructor = true;
7311+
DeclAccessPair Cand = DeclAccessPair::make(CandDecl, clang::AS_none);
7312+
auto CtorInfo = getConstructorInfo(Cand);
7313+
if (CXXMethodDecl *M = dyn_cast<CXXMethodDecl>(Cand->getUnderlyingDecl())) {
7314+
if (Assign)
7315+
SemaRef.AddMethodCandidate(M, Cand, RD, ThisTy, Classification,
7316+
llvm::ArrayRef(&Arg, NumArgs), OCS, true);
7317+
else {
7318+
assert(CtorInfo);
7319+
SemaRef.AddOverloadCandidate(CtorInfo.Constructor, CtorInfo.FoundDecl,
7320+
llvm::ArrayRef(&Arg, NumArgs), OCS,
7321+
/*SuppressUserConversions*/ true);
7322+
}
7323+
} else if (FunctionTemplateDecl *Tmpl =
7324+
dyn_cast<FunctionTemplateDecl>(Cand->getUnderlyingDecl())) {
7325+
if (Assign)
7326+
SemaRef.AddMethodTemplateCandidate(
7327+
Tmpl, Cand, RD, nullptr, ThisTy, Classification,
7328+
llvm::ArrayRef(&Arg, NumArgs), OCS, true);
7329+
else {
7330+
assert(CtorInfo);
7331+
SemaRef.AddTemplateOverloadCandidate(
7332+
CtorInfo.ConstructorTmpl, CtorInfo.FoundDecl, nullptr,
7333+
llvm::ArrayRef(&Arg, NumArgs), OCS, true);
7334+
}
72927335
}
72937336
}
72947337

7295-
if (HasMoveConstructor)
7296-
return !HasDeletedMoveConstructor &&
7297-
(AllowUserDefined ? true : HasDefaultedMoveConstructor);
7298-
return HasCopyConstructor && !HasDeletedCopyConstructor &&
7299-
(AllowUserDefined ? true : HasDefaultedCopyConstructor);
7338+
OverloadCandidateSet::iterator Best;
7339+
switch (OCS.BestViableFunction(SemaRef, LookupLoc, Best)) {
7340+
case OR_Success:
7341+
return cast<CXXMethodDecl>(Best->Function);
7342+
default:
7343+
return nullptr;
7344+
}
73007345
}
73017346

7302-
static bool
7303-
hasSuitableMoveAssignmentOperatorForRelocation(CXXRecordDecl *D,
7304-
bool AllowUserDefined) {
7347+
static bool hasSuitableConstructorForRelocation(Sema &SemaRef, CXXRecordDecl *D,
7348+
bool AllowUserDefined) {
73057349
assert(D->hasDefinition() && !D->isInvalidDecl());
73067350

7307-
if (D->hasExplicitlyDeletedMoveAssignment())
7308-
return false;
7351+
if (D->hasSimpleMoveConstructor() || D->hasSimpleCopyConstructor())
7352+
return true;
73097353

7310-
bool HasDeletedMoveAssignment = false;
7311-
bool HasDeletedCopyAssignment = false;
7312-
bool HasMoveAssignment = D->needsImplicitMoveAssignment();
7313-
bool HasDefaultedMoveAssignment = D->needsImplicitMoveAssignment();
7314-
bool HasDefaultedCopyAssignment = D->needsImplicitCopyAssignment();
7354+
CXXMethodDecl *Decl =
7355+
LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/false);
7356+
return Decl && Decl->isUserProvided() == AllowUserDefined;
7357+
}
73157358

7316-
for (const Decl *D : D->decls()) {
7317-
auto *MD = dyn_cast<CXXMethodDecl>(D);
7318-
if (!MD || MD->isIneligibleOrNotSelected())
7319-
continue;
7359+
static bool
7360+
hasSuitableMoveAssignmentOperatorForRelocation(Sema &SemaRef, CXXRecordDecl *D,
7361+
bool AllowUserDefined) {
7362+
assert(D->hasDefinition() && !D->isInvalidDecl());
73207363

7321-
if (MD->isMoveAssignmentOperator()) {
7322-
HasMoveAssignment = true;
7323-
if (MD->isDefaulted())
7324-
HasDefaultedMoveAssignment = true;
7325-
if (MD->isDeleted())
7326-
HasDeletedMoveAssignment = true;
7327-
} else if (MD->isCopyAssignmentOperator()) {
7328-
if (MD->isDefaulted())
7329-
HasDefaultedCopyAssignment = true;
7330-
if (MD->isDeleted())
7331-
HasDeletedCopyAssignment = true;
7332-
}
7333-
}
7364+
if (D->hasSimpleMoveAssignment() || D->hasSimpleCopyAssignment())
7365+
return true;
73347366

7335-
if (HasMoveAssignment)
7336-
return !HasDeletedMoveAssignment &&
7337-
(AllowUserDefined ? true : HasDefaultedMoveAssignment);
7338-
return !HasDeletedCopyAssignment &&
7339-
(AllowUserDefined ? true : HasDefaultedCopyAssignment);
7367+
CXXMethodDecl *Decl =
7368+
LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/true);
7369+
if (!Decl)
7370+
return false;
7371+
7372+
return Decl && Decl->isUserProvided() == AllowUserDefined;
73407373
}
73417374

73427375
// [C++26][class.prop]
@@ -7348,12 +7381,13 @@ hasSuitableMoveAssignmentOperatorForRelocation(CXXRecordDecl *D,
73487381
// type C selects an assignment operator function that is a direct member of C
73497382
// and is neither user-provided nor deleted, and C has a destructor that is
73507383
// neither user-provided nor deleted.
7351-
static bool isDefaultMovable(CXXRecordDecl *D) {
7352-
if (!hasSuitableConstructorForRelocation(D, /*AllowUserDefined=*/false))
7384+
static bool isDefaultMovable(Sema &SemaRef, CXXRecordDecl *D) {
7385+
if (!hasSuitableConstructorForRelocation(SemaRef, D,
7386+
/*AllowUserDefined=*/false))
73537387
return false;
73547388

73557389
if (!hasSuitableMoveAssignmentOperatorForRelocation(
7356-
D, /*AllowUserDefined=*/false))
7390+
SemaRef, D, /*AllowUserDefined=*/false))
73577391
return false;
73587392

73597393
const auto *Dtr = D->getDestructor();
@@ -7395,53 +7429,15 @@ static bool isEligibleForTrivialRelocation(Sema &SemaRef, CXXRecordDecl *D) {
73957429
continue;
73967430
// ... has a non-static data member of an object type that is not
73977431
// of a trivially relocatable type
7398-
QualType T = SemaRef.getASTContext().getBaseElementType(
7399-
Field->getType().getUnqualifiedType());
7400-
if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) {
7401-
if (!RD->isTriviallyRelocatable())
7402-
return false;
7403-
}
7432+
if (!Field->getType().isCppTriviallyRelocatableType(
7433+
SemaRef.getASTContext()))
7434+
return false;
74047435
}
74057436

74067437
// ...has a deleted destructor
74077438
return !hasDeletedDestructor(D);
74087439
}
74097440

7410-
// [C++26][class.prop]
7411-
// A class C is a trivially relocatable class if
7412-
void Sema::CheckCXX2CTriviallyRelocatable(CXXRecordDecl *D) {
7413-
if (!getLangOpts().CPlusPlus || D->isInvalidDecl())
7414-
return;
7415-
7416-
assert(D->hasDefinition());
7417-
7418-
bool MarkedTriviallyRelocatable =
7419-
D->getTriviallyRelocatableSpecifier().isSet();
7420-
7421-
bool IsTriviallyRelocatable = [&] {
7422-
// if it is eligible for trivial relocation
7423-
7424-
if (!isEligibleForTrivialRelocation(*this, D))
7425-
return false;
7426-
7427-
// has the trivially_relocatable_if_eligible class-property-specifier,
7428-
if (D->isDependentType() || MarkedTriviallyRelocatable)
7429-
return true;
7430-
7431-
// is a union with no user-declared special member functions, or
7432-
if (D->isUnion() && !D->hasUserDeclaredCopyConstructor() &&
7433-
!D->hasUserDeclaredCopyAssignment() &&
7434-
!D->hasUserDeclaredMoveOperation() && !D->hasUserDeclaredDestructor()) {
7435-
return true;
7436-
}
7437-
7438-
// is default-movable.
7439-
return isDefaultMovable(D);
7440-
}();
7441-
7442-
D->setIsTriviallyRelocatable(IsTriviallyRelocatable);
7443-
}
7444-
74457441
// [C++26][class.prop]
74467442
// A class C is eligible for replacement unless
74477443
static bool isEligibleForReplacement(Sema &SemaRef, CXXRecordDecl *D) {
@@ -7468,41 +7464,81 @@ static bool isEligibleForReplacement(Sema &SemaRef, CXXRecordDecl *D) {
74687464
return !hasDeletedDestructor(D);
74697465
}
74707466

7471-
// [C++26][class.prop] A class C is a replaceable class if...
7472-
void Sema::CheckCXX2CReplaceable(CXXRecordDecl *D) {
7467+
void Sema::CheckCXX2CRelocatableAndReplaceable(CXXRecordDecl *D) {
74737468
if (!getLangOpts().CPlusPlus || D->isInvalidDecl())
74747469
return;
74757470

74767471
assert(D->hasDefinition());
74777472

74787473
bool MarkedCXX2CReplaceable = D->getReplaceableSpecifier().isSet();
7474+
bool MarkedTriviallyRelocatable =
7475+
D->getTriviallyRelocatableSpecifier().isSet();
74797476

74807477
// This is part of "eligible for replacement", however we defer it
74817478
// to avoid extraneous computations.
74827479
auto HasSuitableSMP = [&] {
7483-
return hasSuitableConstructorForRelocation(D, /*AllowUserDefined=*/true) &&
7480+
return hasSuitableConstructorForRelocation(*this, D,
7481+
/*AllowUserDefined=*/true) &&
74847482
hasSuitableMoveAssignmentOperatorForRelocation(
7485-
D, /*AllowUserDefined=*/true);
7483+
*this, D, /*AllowUserDefined=*/true);
7484+
};
7485+
7486+
auto IsUnion = [&, Is = std::optional<bool>{}]() mutable {
7487+
if (!Is.has_value())
7488+
Is = D->isUnion() && !D->hasUserDeclaredCopyConstructor() &&
7489+
!D->hasUserDeclaredCopyAssignment() &&
7490+
!D->hasUserDeclaredMoveOperation() &&
7491+
!D->hasUserDeclaredDestructor();
7492+
return *Is;
74867493
};
74877494

7495+
auto IsDefaultMovable = [&, Is = std::optional<bool>{}]() mutable {
7496+
if (!Is.has_value())
7497+
Is = isDefaultMovable(*this, D);
7498+
return *Is;
7499+
};
7500+
7501+
bool IsTriviallyRelocatable = [&] {
7502+
if (D->isDependentType())
7503+
return false;
7504+
7505+
// if it is eligible for trivial relocation
7506+
if (!isEligibleForTrivialRelocation(*this, D))
7507+
return false;
7508+
7509+
// has the trivially_relocatable_if_eligible class-property-specifier,
7510+
if (MarkedTriviallyRelocatable)
7511+
return true;
7512+
7513+
// is a union with no user-declared special member functions, or
7514+
if (IsUnion()) {
7515+
return true;
7516+
}
7517+
// is default-movable.
7518+
return IsDefaultMovable();
7519+
}();
7520+
7521+
D->setIsTriviallyRelocatable(IsTriviallyRelocatable);
7522+
74887523
bool IsReplaceable = [&] {
7524+
if (D->isDependentType())
7525+
return false;
7526+
74897527
// A class C is a replaceable class if it is eligible for replacement
74907528
if (!isEligibleForReplacement(*this, D))
74917529
return false;
74927530

74937531
// has the replaceable_if_eligible class-property-specifier
7494-
if (D->isDependentType() || MarkedCXX2CReplaceable)
7532+
if (MarkedCXX2CReplaceable)
74957533
return HasSuitableSMP();
74967534

74977535
// is a union with no user-declared special member functions, or
7498-
if (D->isUnion() && !D->hasUserDeclaredCopyConstructor() &&
7499-
!D->hasUserDeclaredCopyAssignment() &&
7500-
!D->hasUserDeclaredMoveOperation() && !D->hasUserDeclaredDestructor()) {
7536+
if (IsUnion()) {
75017537
return HasSuitableSMP();
75027538
}
75037539

75047540
// is default-movable.
7505-
return isDefaultMovable(D);
7541+
return IsDefaultMovable();
75067542
}();
75077543

75087544
D->setIsReplaceable(IsReplaceable);

0 commit comments

Comments
 (0)