@@ -1959,6 +1959,7 @@ static std::optional<TypeTrait> StdNameToTypeTrait(StringRef Name) {
19591959 .Case (" is_trivially_copyable" , TypeTrait::UTT_IsTriviallyCopyable)
19601960 .Case (" is_assignable" , TypeTrait::BTT_IsAssignable)
19611961 .Case (" is_empty" , TypeTrait::UTT_IsEmpty)
1962+ .Case (" is_standard_layout" , TypeTrait::UTT_IsStandardLayout)
19621963 .Default (std::nullopt );
19631964}
19641965
@@ -2382,6 +2383,150 @@ static void DiagnoseIsEmptyReason(Sema &S, SourceLocation Loc, QualType T) {
23822383 }
23832384}
23842385
2386+ static bool hasMultipleDataBaseClassesWithFields (const CXXRecordDecl *D) {
2387+ int NumBasesWithFields = 0 ;
2388+ for (const CXXBaseSpecifier &Base : D->bases ()) {
2389+ const CXXRecordDecl *BaseRD = Base.getType ()->getAsCXXRecordDecl ();
2390+ if (!BaseRD || BaseRD->isInvalidDecl ())
2391+ continue ;
2392+
2393+ for (const FieldDecl *Field : BaseRD->fields ()) {
2394+ if (!Field->isUnnamedBitField ()) {
2395+ if (++NumBasesWithFields > 1 )
2396+ return true ; // found more than one base class with fields
2397+ break ; // no need to check further fields in this base class
2398+ }
2399+ }
2400+ }
2401+ return false ;
2402+ }
2403+
2404+ static void DiagnoseNonStandardLayoutReason (Sema &SemaRef, SourceLocation Loc,
2405+ const CXXRecordDecl *D) {
2406+ for (const CXXBaseSpecifier &B : D->bases ()) {
2407+ assert (B.getType ()->getAsCXXRecordDecl () && " invalid base?" );
2408+ if (B.isVirtual ()) {
2409+ SemaRef.Diag (Loc, diag::note_unsatisfied_trait_reason)
2410+ << diag::TraitNotSatisfiedReason::VBase << B.getType ()
2411+ << B.getSourceRange ();
2412+ }
2413+ if (!B.getType ()->isStandardLayoutType ()) {
2414+ SemaRef.Diag (Loc, diag::note_unsatisfied_trait_reason)
2415+ << diag::TraitNotSatisfiedReason::NonStandardLayoutBase << B.getType ()
2416+ << B.getSourceRange ();
2417+ }
2418+ }
2419+ // Check for mixed access specifiers in fields.
2420+ const FieldDecl *FirstField = nullptr ;
2421+ AccessSpecifier FirstAccess = AS_none;
2422+
2423+ for (const FieldDecl *Field : D->fields ()) {
2424+ if (Field->isUnnamedBitField ())
2425+ continue ;
2426+
2427+ // Record the first field we see
2428+ if (!FirstField) {
2429+ FirstField = Field;
2430+ FirstAccess = Field->getAccess ();
2431+ continue ;
2432+ }
2433+
2434+ // Check if the field has a different access specifier than the first one.
2435+ if (Field->getAccess () != FirstAccess) {
2436+ // Emit a diagnostic about mixed access specifiers.
2437+ SemaRef.Diag (Loc, diag::note_unsatisfied_trait_reason)
2438+ << diag::TraitNotSatisfiedReason::MixedAccess;
2439+
2440+ SemaRef.Diag (FirstField->getLocation (), diag::note_defined_here)
2441+ << FirstField;
2442+
2443+ SemaRef.Diag (Field->getLocation (), diag::note_unsatisfied_trait_reason)
2444+ << diag::TraitNotSatisfiedReason::MixedAccessField << Field
2445+ << FirstField;
2446+
2447+ // No need to check further fields, as we already found mixed access.
2448+ break ;
2449+ }
2450+ }
2451+ if (hasMultipleDataBaseClassesWithFields (D)) {
2452+ SemaRef.Diag (Loc, diag::note_unsatisfied_trait_reason)
2453+ << diag::TraitNotSatisfiedReason::MultipleDataBase;
2454+ }
2455+ if (D->isPolymorphic ()) {
2456+ // Find the best location to point “defined here” at.
2457+ const CXXMethodDecl *VirtualMD = nullptr ;
2458+ // First, look for a virtual method.
2459+ for (const auto *M : D->methods ()) {
2460+ if (M->isVirtual ()) {
2461+ VirtualMD = M;
2462+ break ;
2463+ }
2464+ }
2465+ if (VirtualMD) {
2466+ SemaRef.Diag (Loc, diag::note_unsatisfied_trait_reason)
2467+ << diag::TraitNotSatisfiedReason::VirtualFunction << VirtualMD;
2468+ SemaRef.Diag (VirtualMD->getLocation (), diag::note_defined_here)
2469+ << VirtualMD;
2470+ } else {
2471+ // If no virtual method, point to the record declaration itself.
2472+ SemaRef.Diag (Loc, diag::note_unsatisfied_trait_reason)
2473+ << diag::TraitNotSatisfiedReason::VirtualFunction << D;
2474+ SemaRef.Diag (D->getLocation (), diag::note_defined_here) << D;
2475+ }
2476+ }
2477+ for (const FieldDecl *Field : D->fields ()) {
2478+ if (!Field->getType ()->isStandardLayoutType ()) {
2479+ SemaRef.Diag (Loc, diag::note_unsatisfied_trait_reason)
2480+ << diag::TraitNotSatisfiedReason::NonStandardLayoutMember << Field
2481+ << Field->getType () << Field->getSourceRange ();
2482+ }
2483+ }
2484+ // Find any indirect base classes that have fields.
2485+ if (D->hasDirectFields ()) {
2486+ const CXXRecordDecl *Indirect = nullptr ;
2487+ D->forallBases ([&](const CXXRecordDecl *BaseDef) {
2488+ if (BaseDef->hasDirectFields ()) {
2489+ Indirect = BaseDef;
2490+ return false ; // stop traversal
2491+ }
2492+ return true ; // continue to the next base
2493+ });
2494+ if (Indirect) {
2495+ SemaRef.Diag (Loc, diag::note_unsatisfied_trait_reason)
2496+ << diag::TraitNotSatisfiedReason::IndirectBaseWithFields << Indirect
2497+ << Indirect->getSourceRange ();
2498+ }
2499+ }
2500+ }
2501+
2502+ static void DiagnoseNonStandardLayoutReason (Sema &SemaRef, SourceLocation Loc,
2503+ QualType T) {
2504+ SemaRef.Diag (Loc, diag::note_unsatisfied_trait)
2505+ << T << diag::TraitName::StandardLayout;
2506+
2507+ // Check type-level exclusion first.
2508+ if (T->isVariablyModifiedType ()) {
2509+ SemaRef.Diag (Loc, diag::note_unsatisfied_trait_reason)
2510+ << diag::TraitNotSatisfiedReason::VLA;
2511+ return ;
2512+ }
2513+
2514+ if (T->isReferenceType ()) {
2515+ SemaRef.Diag (Loc, diag::note_unsatisfied_trait_reason)
2516+ << diag::TraitNotSatisfiedReason::Ref;
2517+ return ;
2518+ }
2519+ T = T.getNonReferenceType ();
2520+ const CXXRecordDecl *D = T->getAsCXXRecordDecl ();
2521+ if (!D || D->isInvalidDecl ())
2522+ return ;
2523+
2524+ if (D->hasDefinition ())
2525+ DiagnoseNonStandardLayoutReason (SemaRef, Loc, D);
2526+
2527+ SemaRef.Diag (D->getLocation (), diag::note_defined_here) << D;
2528+ }
2529+
23852530void Sema::DiagnoseTypeTraitDetails (const Expr *E) {
23862531 E = E->IgnoreParenImpCasts ();
23872532 if (E->containsErrors ())
@@ -2408,6 +2553,9 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
24082553 case UTT_IsEmpty:
24092554 DiagnoseIsEmptyReason (*this , E->getBeginLoc (), Args[0 ]);
24102555 break ;
2556+ case UTT_IsStandardLayout:
2557+ DiagnoseNonStandardLayoutReason (*this , E->getBeginLoc (), Args[0 ]);
2558+ break ;
24112559 default :
24122560 break ;
24132561 }
0 commit comments