Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions clang/lib/Sema/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ add_clang_library(clangSema
SemaTemplateInstantiateDecl.cpp
SemaTemplateVariadic.cpp
SemaType.cpp
SemaTypeTraits.cpp
SemaWasm.cpp
SemaX86.cpp
TypeLocBuilder.cpp
Expand Down
273 changes: 0 additions & 273 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7368,279 +7368,6 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) {
CheckMismatchedTypeAwareAllocators(OO_Array_New, OO_Array_Delete);
}

static CXXMethodDecl *LookupSpecialMemberFromXValue(Sema &SemaRef,
const CXXRecordDecl *RD,
bool Assign) {
RD = RD->getDefinition();
SourceLocation LookupLoc = RD->getLocation();

CanQualType CanTy = SemaRef.getASTContext().getCanonicalType(
SemaRef.getASTContext().getTagDeclType(RD));
DeclarationName Name;
Expr *Arg = nullptr;
unsigned NumArgs;

QualType ArgType = CanTy;
ExprValueKind VK = clang::VK_XValue;

if (Assign)
Name =
SemaRef.getASTContext().DeclarationNames.getCXXOperatorName(OO_Equal);
else
Name =
SemaRef.getASTContext().DeclarationNames.getCXXConstructorName(CanTy);

OpaqueValueExpr FakeArg(LookupLoc, ArgType, VK);
NumArgs = 1;
Arg = &FakeArg;

// Create the object argument
QualType ThisTy = CanTy;
Expr::Classification Classification =
OpaqueValueExpr(LookupLoc, ThisTy, VK_LValue)
.Classify(SemaRef.getASTContext());

// Now we perform lookup on the name we computed earlier and do overload
// resolution. Lookup is only performed directly into the class since there
// will always be a (possibly implicit) declaration to shadow any others.
OverloadCandidateSet OCS(LookupLoc, OverloadCandidateSet::CSK_Normal);
DeclContext::lookup_result R = RD->lookup(Name);

if (R.empty())
return nullptr;

// Copy the candidates as our processing of them may load new declarations
// from an external source and invalidate lookup_result.
SmallVector<NamedDecl *, 8> Candidates(R.begin(), R.end());

for (NamedDecl *CandDecl : Candidates) {
if (CandDecl->isInvalidDecl())
continue;

DeclAccessPair Cand = DeclAccessPair::make(CandDecl, clang::AS_none);
auto CtorInfo = getConstructorInfo(Cand);
if (CXXMethodDecl *M = dyn_cast<CXXMethodDecl>(Cand->getUnderlyingDecl())) {
if (Assign)
SemaRef.AddMethodCandidate(M, Cand, const_cast<CXXRecordDecl *>(RD),
ThisTy, Classification,
llvm::ArrayRef(&Arg, NumArgs), OCS, true);
else {
assert(CtorInfo);
SemaRef.AddOverloadCandidate(CtorInfo.Constructor, CtorInfo.FoundDecl,
llvm::ArrayRef(&Arg, NumArgs), OCS,
/*SuppressUserConversions*/ true);
}
} else if (FunctionTemplateDecl *Tmpl =
dyn_cast<FunctionTemplateDecl>(Cand->getUnderlyingDecl())) {
if (Assign)
SemaRef.AddMethodTemplateCandidate(
Tmpl, Cand, const_cast<CXXRecordDecl *>(RD), nullptr, ThisTy,
Classification, llvm::ArrayRef(&Arg, NumArgs), OCS, true);
else {
assert(CtorInfo);
SemaRef.AddTemplateOverloadCandidate(
CtorInfo.ConstructorTmpl, CtorInfo.FoundDecl, nullptr,
llvm::ArrayRef(&Arg, NumArgs), OCS, true);
}
}
}

OverloadCandidateSet::iterator Best;
switch (OCS.BestViableFunction(SemaRef, LookupLoc, Best)) {
case OR_Success:
return cast<CXXMethodDecl>(Best->Function);
default:
return nullptr;
}
}

static bool hasSuitableConstructorForRelocation(Sema &SemaRef,
const CXXRecordDecl *D,
bool AllowUserDefined) {
assert(D->hasDefinition() && !D->isInvalidDecl());

if (D->hasSimpleMoveConstructor() || D->hasSimpleCopyConstructor())
return true;

CXXMethodDecl *Decl =
LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/false);
return Decl && Decl->isUserProvided() == AllowUserDefined;
}

static bool hasSuitableMoveAssignmentOperatorForRelocation(
Sema &SemaRef, const CXXRecordDecl *D, bool AllowUserDefined) {
assert(D->hasDefinition() && !D->isInvalidDecl());

if (D->hasSimpleMoveAssignment() || D->hasSimpleCopyAssignment())
return true;

CXXMethodDecl *Decl =
LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/true);
if (!Decl)
return false;

return Decl && Decl->isUserProvided() == AllowUserDefined;
}

// [C++26][class.prop]
// A class C is default-movable if
// - overload resolution for direct-initializing an object of type C
// from an xvalue of type C selects a constructor that is a direct member of C
// and is neither user-provided nor deleted,
// - overload resolution for assigning to an lvalue of type C from an xvalue of
// type C selects an assignment operator function that is a direct member of C
// and is neither user-provided nor deleted, and C has a destructor that is
// neither user-provided nor deleted.
static bool IsDefaultMovable(Sema &SemaRef, const CXXRecordDecl *D) {
if (!hasSuitableConstructorForRelocation(SemaRef, D,
/*AllowUserDefined=*/false))
return false;

if (!hasSuitableMoveAssignmentOperatorForRelocation(
SemaRef, D, /*AllowUserDefined=*/false))
return false;

CXXDestructorDecl *Dtr = D->getDestructor();

if (!Dtr)
return true;

if (Dtr->isUserProvided() && (!Dtr->isDefaulted() || Dtr->isDeleted()))
return false;

return !Dtr->isDeleted();
}

// [C++26][class.prop]
// A class is eligible for trivial relocation unless it...
static bool IsEligibleForTrivialRelocation(Sema &SemaRef,
const CXXRecordDecl *D) {

for (const CXXBaseSpecifier &B : D->bases()) {
const auto *BaseDecl = B.getType()->getAsCXXRecordDecl();
if (!BaseDecl)
continue;
// ... has any virtual base classes
// ... has a base class that is not a trivially relocatable class
if (B.isVirtual() || (!BaseDecl->isDependentType() &&
!SemaRef.IsCXXTriviallyRelocatableType(B.getType())))
return false;
}

for (const FieldDecl *Field : D->fields()) {
if (Field->getType()->isDependentType())
continue;
if (Field->getType()->isReferenceType())
continue;
// ... has a non-static data member of an object type that is not
// of a trivially relocatable type
if (!SemaRef.IsCXXTriviallyRelocatableType(Field->getType()))
return false;
}
return !D->hasDeletedDestructor();
}

// [C++26][class.prop]
// A class C is eligible for replacement unless
static bool IsEligibleForReplacement(Sema &SemaRef, const CXXRecordDecl *D) {

for (const CXXBaseSpecifier &B : D->bases()) {
const auto *BaseDecl = B.getType()->getAsCXXRecordDecl();
if (!BaseDecl)
continue;
// it has a base class that is not a replaceable class
if (!BaseDecl->isDependentType() &&
!SemaRef.IsCXXReplaceableType(B.getType()))
return false;
}

for (const FieldDecl *Field : D->fields()) {
if (Field->getType()->isDependentType())
continue;

// it has a non-static data member that is not of a replaceable type,
if (!SemaRef.IsCXXReplaceableType(Field->getType()))
return false;
}
return !D->hasDeletedDestructor();
}

ASTContext::CXXRecordDeclRelocationInfo
Sema::CheckCXX2CRelocatableAndReplaceable(const CXXRecordDecl *D) {
ASTContext::CXXRecordDeclRelocationInfo Info{false, false};

if (!getLangOpts().CPlusPlus || D->isInvalidDecl())
return Info;

assert(D->hasDefinition());

// This is part of "eligible for replacement", however we defer it
// to avoid extraneous computations.
auto HasSuitableSMP = [&] {
return hasSuitableConstructorForRelocation(*this, D,
/*AllowUserDefined=*/true) &&
hasSuitableMoveAssignmentOperatorForRelocation(
*this, D, /*AllowUserDefined=*/true);
};

auto IsUnion = [&, Is = std::optional<bool>{}]() mutable {
if (!Is.has_value())
Is = D->isUnion() && !D->hasUserDeclaredCopyConstructor() &&
!D->hasUserDeclaredCopyAssignment() &&
!D->hasUserDeclaredMoveOperation() &&
!D->hasUserDeclaredDestructor();
return *Is;
};

auto IsDefaultMovable = [&, Is = std::optional<bool>{}]() mutable {
if (!Is.has_value())
Is = ::IsDefaultMovable(*this, D);
return *Is;
};

Info.IsRelocatable = [&] {
if (D->isDependentType())
return false;

// if it is eligible for trivial relocation
if (!IsEligibleForTrivialRelocation(*this, D))
return false;

// has the trivially_relocatable_if_eligible class-property-specifier,
if (D->hasAttr<TriviallyRelocatableAttr>())
return true;

// is a union with no user-declared special member functions, or
if (IsUnion())
return true;

// is default-movable.
return IsDefaultMovable();
}();

Info.IsReplaceable = [&] {
if (D->isDependentType())
return false;

// A class C is a replaceable class if it is eligible for replacement
if (!IsEligibleForReplacement(*this, D))
return false;

// has the replaceable_if_eligible class-property-specifier
if (D->hasAttr<ReplaceableAttr>())
return HasSuitableSMP();

// is a union with no user-declared special member functions, or
if (IsUnion())
return HasSuitableSMP();

// is default-movable.
return IsDefaultMovable();
}();

return Info;
}

/// Look up the special member function that would be called by a special
/// member function for a subobject of class type.
///
Expand Down
Loading
Loading