@@ -7221,8 +7221,7 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) {
7221
7221
checkClassLevelDLLAttribute(Record);
7222
7222
checkClassLevelCodeSegAttribute(Record);
7223
7223
7224
- CheckCXX2CTriviallyRelocatable(Record);
7225
- CheckCXX2CReplaceable(Record);
7224
+ CheckCXX2CRelocatableAndReplaceable(Record);
7226
7225
7227
7226
bool ClangABICompat4 =
7228
7227
Context.getLangOpts().getClangABICompat() <= LangOptions::ClangABI::Ver4;
@@ -7261,82 +7260,116 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) {
7261
7260
}
7262
7261
}
7263
7262
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;
7267
7302
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());
7274
7306
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())
7278
7309
continue;
7279
7310
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
+ }
7292
7335
}
7293
7336
}
7294
7337
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
+ }
7300
7345
}
7301
7346
7302
- static bool
7303
- hasSuitableMoveAssignmentOperatorForRelocation(CXXRecordDecl *D,
7304
- bool AllowUserDefined) {
7347
+ static bool hasSuitableConstructorForRelocation(Sema &SemaRef, CXXRecordDecl *D,
7348
+ bool AllowUserDefined) {
7305
7349
assert(D->hasDefinition() && !D->isInvalidDecl());
7306
7350
7307
- if (D->hasExplicitlyDeletedMoveAssignment ())
7308
- return false ;
7351
+ if (D->hasSimpleMoveConstructor() || D->hasSimpleCopyConstructor ())
7352
+ return true ;
7309
7353
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
+ }
7315
7358
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()) ;
7320
7363
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;
7334
7366
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;
7340
7373
}
7341
7374
7342
7375
// [C++26][class.prop]
@@ -7348,12 +7381,13 @@ hasSuitableMoveAssignmentOperatorForRelocation(CXXRecordDecl *D,
7348
7381
// type C selects an assignment operator function that is a direct member of C
7349
7382
// and is neither user-provided nor deleted, and C has a destructor that is
7350
7383
// 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))
7353
7387
return false;
7354
7388
7355
7389
if (!hasSuitableMoveAssignmentOperatorForRelocation(
7356
- D, /*AllowUserDefined=*/false))
7390
+ SemaRef, D, /*AllowUserDefined=*/false))
7357
7391
return false;
7358
7392
7359
7393
const auto *Dtr = D->getDestructor();
@@ -7395,53 +7429,15 @@ static bool isEligibleForTrivialRelocation(Sema &SemaRef, CXXRecordDecl *D) {
7395
7429
continue;
7396
7430
// ... has a non-static data member of an object type that is not
7397
7431
// 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;
7404
7435
}
7405
7436
7406
7437
// ...has a deleted destructor
7407
7438
return !hasDeletedDestructor(D);
7408
7439
}
7409
7440
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
-
7445
7441
// [C++26][class.prop]
7446
7442
// A class C is eligible for replacement unless
7447
7443
static bool isEligibleForReplacement(Sema &SemaRef, CXXRecordDecl *D) {
@@ -7468,41 +7464,81 @@ static bool isEligibleForReplacement(Sema &SemaRef, CXXRecordDecl *D) {
7468
7464
return !hasDeletedDestructor(D);
7469
7465
}
7470
7466
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) {
7473
7468
if (!getLangOpts().CPlusPlus || D->isInvalidDecl())
7474
7469
return;
7475
7470
7476
7471
assert(D->hasDefinition());
7477
7472
7478
7473
bool MarkedCXX2CReplaceable = D->getReplaceableSpecifier().isSet();
7474
+ bool MarkedTriviallyRelocatable =
7475
+ D->getTriviallyRelocatableSpecifier().isSet();
7479
7476
7480
7477
// This is part of "eligible for replacement", however we defer it
7481
7478
// to avoid extraneous computations.
7482
7479
auto HasSuitableSMP = [&] {
7483
- return hasSuitableConstructorForRelocation(D, /*AllowUserDefined=*/true) &&
7480
+ return hasSuitableConstructorForRelocation(*this, D,
7481
+ /*AllowUserDefined=*/true) &&
7484
7482
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;
7486
7493
};
7487
7494
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
+
7488
7523
bool IsReplaceable = [&] {
7524
+ if (D->isDependentType())
7525
+ return false;
7526
+
7489
7527
// A class C is a replaceable class if it is eligible for replacement
7490
7528
if (!isEligibleForReplacement(*this, D))
7491
7529
return false;
7492
7530
7493
7531
// has the replaceable_if_eligible class-property-specifier
7494
- if (D->isDependentType() || MarkedCXX2CReplaceable)
7532
+ if (MarkedCXX2CReplaceable)
7495
7533
return HasSuitableSMP();
7496
7534
7497
7535
// 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()) {
7501
7537
return HasSuitableSMP();
7502
7538
}
7503
7539
7504
7540
// is default-movable.
7505
- return isDefaultMovable(D );
7541
+ return IsDefaultMovable( );
7506
7542
}();
7507
7543
7508
7544
D->setIsReplaceable(IsReplaceable);
0 commit comments