https://github.com/cor3ntin updated https://github.com/llvm/llvm-project/pull/127636
>From 8f000e3fd9c93d0c7a7a9ce8b12999c70c4d4b1a Mon Sep 17 00:00:00 2001 From: Corentin Jabot <corentinja...@gmail.com> Date: Tue, 18 Feb 2025 14:37:32 +0100 Subject: [PATCH] [Clang][WIP] Trivial relocation --- .../clang/AST/CXXRecordDeclDefinitionBits.def | 8 + clang/include/clang/AST/DeclCXX.h | 81 ++++++- clang/include/clang/AST/Type.h | 4 + clang/include/clang/Basic/Builtins.td | 6 + .../clang/Basic/DiagnosticParseKinds.td | 3 + .../clang/Basic/DiagnosticSemaKinds.td | 4 + clang/include/clang/Basic/TokenKinds.def | 2 + clang/include/clang/Parse/Parser.h | 13 ++ clang/include/clang/Sema/Sema.h | 19 +- clang/lib/AST/Decl.cpp | 1 + clang/lib/AST/DeclCXX.cpp | 20 +- clang/lib/AST/Type.cpp | 24 ++ clang/lib/CodeGen/CGBuiltin.cpp | 1 + clang/lib/Frontend/InitPreprocessor.cpp | 3 +- clang/lib/Parse/ParseDeclCXX.cpp | 121 ++++++++-- clang/lib/Parse/Parser.cpp | 2 + clang/lib/Sema/SemaChecking.cpp | 51 +++++ clang/lib/Sema/SemaDecl.cpp | 25 +- clang/lib/Sema/SemaDeclCXX.cpp | 214 ++++++++++++++++++ clang/lib/Sema/SemaExprCXX.cpp | 6 + clang/lib/Sema/SemaTemplateInstantiate.cpp | 4 + 21 files changed, 570 insertions(+), 42 deletions(-) diff --git a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def index 6620840df0ced..7633a987673e9 100644 --- a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def +++ b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def @@ -224,6 +224,10 @@ FIELD(StructuralIfLiteral, 1, NO_MERGE) /// explicitly deleted or defaulted). FIELD(UserProvidedDefaultConstructor, 1, NO_MERGE) +FIELD(UserProvidedMoveAssignment, 1, NO_MERGE) +FIELD(UserProvidedCopyAssignment, 1, NO_MERGE) +FIELD(ExplicitlyDeletedMoveAssignment, 1, NO_MERGE) + /// The special members which have been declared for this class, /// either by the user or implicitly. FIELD(DeclaredSpecialMembers, 6, MERGE_OR) @@ -253,4 +257,8 @@ FIELD(IsAnyDestructorNoReturn, 1, NO_MERGE) /// type that is intangible). HLSL only. FIELD(IsHLSLIntangible, 1, NO_MERGE) +FIELD(IsTriviallyRelocatable, 1, NO_MERGE) + +FIELD(IsReplaceable, 1, NO_MERGE) + #undef FIELD diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index 266b93a64a390..c4701a407e714 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -127,6 +127,33 @@ class AccessSpecDecl : public Decl { static bool classofKind(Kind K) { return K == AccessSpec; } }; +enum class RelocatableOrReplaceableClassSpecifierKind { + Relocatable, + Replaceable +}; + +template <RelocatableOrReplaceableClassSpecifierKind MK> +class BasicRelocatableOrReplaceableClassSpecifier { +public: + BasicRelocatableOrReplaceableClassSpecifier() = default; + BasicRelocatableOrReplaceableClassSpecifier(SourceLocation Begin) + : Loc(Begin) {} + void Set(SourceLocation Begin) { Loc = Begin; } + + bool isSet() const { return !Loc.isInvalid(); } + + SourceLocation getLocation() const { return Loc; } + +private: + SourceLocation Loc; +}; + +using TriviallyRelocatableSpecifier = + BasicRelocatableOrReplaceableClassSpecifier< + RelocatableOrReplaceableClassSpecifierKind::Relocatable>; +using ReplaceableSpecifier = BasicRelocatableOrReplaceableClassSpecifier< + RelocatableOrReplaceableClassSpecifierKind::Replaceable>; + /// Represents a base class of a C++ class. /// /// Each CXXBaseSpecifier represents a single, direct base class (or @@ -349,6 +376,10 @@ class CXXRecordDecl : public RecordDecl { /// This is actually currently stored in reverse order. LazyDeclPtr FirstFriend; + TriviallyRelocatableSpecifier TriviallyRelocatableSpecifier; + + ReplaceableSpecifier ReplaceableSpecifier; + DefinitionData(CXXRecordDecl *D); /// Retrieve the set of direct base classes. @@ -716,12 +747,19 @@ class CXXRecordDecl : public RecordDecl { /// \c true if a defaulted move constructor for this class would be /// deleted. bool defaultedMoveConstructorIsDeleted() const { - assert((!needsOverloadResolutionForMoveConstructor() || - (data().DeclaredSpecialMembers & SMF_MoveConstructor)) && - "this property has not yet been computed by Sema"); + // assert((!needsOverloadResolutionForMoveConstructor() || + // (data().DeclaredSpecialMembers & SMF_MoveConstructor)) && + // "this property has not yet been computed by Sema"); return data().DefaultedMoveConstructorIsDeleted; } + bool defaultedMoveAssignmentIsDeleted() const { + // assert((!needsOverloadResolutionForMoveAssignment() || + // (data().DeclaredSpecialMembers & SMF_MoveAssignment)) && + // "this property has not yet been computed by Sema"); + return data().DefaultedMoveAssignmentIsDeleted; + } + /// \c true if a defaulted destructor for this class would be deleted. bool defaultedDestructorIsDeleted() const { assert((!needsOverloadResolutionForDestructor() || @@ -806,6 +844,18 @@ class CXXRecordDecl : public RecordDecl { return data().UserDeclaredSpecialMembers & SMF_CopyConstructor; } + bool hasUserProvidedCopyAssignment() const { + return data().UserProvidedCopyAssignment; + } + + bool hasUserProvidedMoveAssignment() const { + return data().UserProvidedCopyAssignment; + } + + bool hasExplicitlyDeletedMoveAssignment() const { + return data().ExplicitlyDeletedMoveAssignment; + } + /// Determine whether this class needs an implicit copy /// constructor to be lazily declared. bool needsImplicitCopyConstructor() const { @@ -1471,6 +1521,24 @@ class CXXRecordDecl : public RecordDecl { return isLiteral() && data().StructuralIfLiteral; } + TriviallyRelocatableSpecifier getTriviallyRelocatableSpecifier() const { + return data().TriviallyRelocatableSpecifier; + } + + ReplaceableSpecifier getReplaceableSpecifier() const { + return data().ReplaceableSpecifier; + } + + bool isTriviallyRelocatable() const { return data().IsTriviallyRelocatable; } + + void setIsTriviallyRelocatable(bool Set) { + data().IsTriviallyRelocatable = Set; + } + + bool isReplaceable() const { return data().IsReplaceable; } + + void setIsReplaceable(bool Set) { data().IsReplaceable = Set; } + /// Notify the class that this destructor is now selected. /// /// Important properties of the class depend on destructor properties. Since @@ -1905,6 +1973,13 @@ class CXXRecordDecl : public RecordDecl { return K >= firstCXXRecord && K <= lastCXXRecord; } void markAbstract() { data().Abstract = true; } + + void setTriviallyRelocatableSpecifier(TriviallyRelocatableSpecifier TRS) { + data().TriviallyRelocatableSpecifier = TRS; + } + void setReplaceableSpecifier(ReplaceableSpecifier MRS) { + data().ReplaceableSpecifier = MRS; + } }; /// Store information needed for an explicit specifier. diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 1d9743520654e..8eb373cdd942a 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -1129,6 +1129,10 @@ class QualType { /// Return true if this is a trivially relocatable type. bool isTriviallyRelocatableType(const ASTContext &Context) const; + bool isCppTriviallyRelocatableType(const ASTContext &Context) const; + + bool isReplaceableType(const ASTContext &Context) const; + /// Returns true if it is a class and it might be dynamic. bool mayBeDynamicClass() const; diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 0e5df338dd2e5..237fa3a46f8c8 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -2823,6 +2823,12 @@ def MemMove : LibBuiltin<"string.h"> { let AddBuiltinPrefixedAlias = 1; } +def BuiltinTriviallyRelocate : Builtin { + let Spellings = ["__builtin_trivially_relocate"]; + let Attributes = [FunctionWithBuiltinPrefix, CustomTypeChecking, NoThrow]; + let Prototype = "void*(void*, void*, size_t)"; +} + def StrCpy : LibBuiltin<"string.h"> { let Spellings = ["strcpy"]; let Attributes = [NoThrow]; diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index c513dab810d1f..84ee963e48ff6 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1063,6 +1063,9 @@ def err_access_specifier_interface : Error< def err_duplicate_class_virt_specifier : Error< "class already marked '%0'">; +def err_duplicate_class_relocation_specifier : Error< + "class already marked %select{'trivially_relocatable_if_eligible'|'replaceable_if_eligible'}0">; + def err_duplicate_virt_specifier : Error< "class member already marked '%0'">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index f10af8f5bd6b2..be2a68411ae1a 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -12427,6 +12427,10 @@ def err_builtin_invalid_arg_type: Error < "an 'int'|" "a vector of floating points}1 (was %2)">; +def err_builtin_trivially_relocate_invalid_arg_type: Error < + "argument%select{s|||}0 to '__builtin_trivially_relocate' must be" + "%select{a pointer|non-const|relocatable|the same}0">; + def err_builtin_matrix_disabled: Error< "matrix types extension is disabled. Pass -fenable-matrix to enable it">; def err_matrix_index_not_integer: Error< diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 397a5d95709fb..72dc9cf4ef96e 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -555,6 +555,8 @@ TYPE_TRAIT_2(__reference_converts_from_temporary, ReferenceConvertsFromTemporary TYPE_TRAIT_2(/*EmptySpellingName*/, IsDeducible, KEYCXX) TYPE_TRAIT_1(__is_bitwise_cloneable, IsBitwiseCloneable, KEYALL) +TYPE_TRAIT_1(__is_cpp_trivially_relocatable, IsCppTriviallyRelocatable, KEYCXX) +TYPE_TRAIT_1(__builtin_is_replaceable, IsReplaceable, KEYCXX) // Embarcadero Expression Traits EXPRESSION_TRAIT(__is_lvalue_expr, IsLValueExpr, KEYCXX) diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 335258d597028..304ad6dd25476 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -164,6 +164,8 @@ class Parser : public CodeCompletionHandler { mutable IdentifierInfo *Ident_final; mutable IdentifierInfo *Ident_GNU_final; mutable IdentifierInfo *Ident_override; + mutable IdentifierInfo *Ident_trivially_relocatable_if_eligible; + mutable IdentifierInfo *Ident_replaceable_if_eligible; // C++2a contextual keywords. mutable IdentifierInfo *Ident_import; @@ -3169,6 +3171,17 @@ class Parser : public CodeCompletionHandler { SourceLocation FriendLoc); bool isCXX11FinalKeyword() const; + + bool isCXX2CTriviallyRelocatableKeyword(Token Tok) const; + bool isCXX2CTriviallyRelocatableKeyword() const; + void ParseOptionalCXX2CTriviallyRelocatableSpecifier( + TriviallyRelocatableSpecifier &TRS); + + bool isCXX2CReplaceableKeyword(Token Tok) const; + bool isCXX2CReplaceableKeyword() const; + void ParseOptionalCXX2CReplaceableSpecifier(ReplaceableSpecifier &MRS); + + bool isClassCompatibleKeyword(Token Tok) const; bool isClassCompatibleKeyword() const; /// DeclaratorScopeObj - RAII object used in Parser::ParseDirectDeclarator to diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index c55b964650323..4fd14d2b93636 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3960,20 +3960,29 @@ class Sema final : public SemaBase { /// Invoked when we enter a tag definition that we're skipping. SkippedDefinitionContext ActOnTagStartSkippedDefinition(Scope *S, Decl *TD); + TriviallyRelocatableSpecifier + ActOnTriviallyRelocatableSpecifier(SourceLocation Loc); + + ReplaceableSpecifier ActOnReplaceableSpecifier(SourceLocation Loc); + /// ActOnStartCXXMemberDeclarations - Invoked when we have parsed a /// C++ record definition's base-specifiers clause and are starting its /// member declarations. - void ActOnStartCXXMemberDeclarations(Scope *S, Decl *TagDecl, - SourceLocation FinalLoc, - bool IsFinalSpelledSealed, - bool IsAbstract, - SourceLocation LBraceLoc); + void ActOnStartCXXMemberDeclarations( + Scope *S, Decl *TagDecl, SourceLocation FinalLoc, + bool IsFinalSpelledSealed, bool IsAbstract, + TriviallyRelocatableSpecifier TriviallyRelocatable, + ReplaceableSpecifier Replaceable, SourceLocation LBraceLoc); /// ActOnTagFinishDefinition - Invoked once we have finished parsing /// the definition of a tag (enumeration, class, struct, or union). void ActOnTagFinishDefinition(Scope *S, Decl *TagDecl, SourceRange BraceRange); + void CheckCXX2CTriviallyRelocatable(CXXRecordDecl *D); + + void CheckCXX2CReplaceable(CXXRecordDecl *D); + void ActOnTagFinishSkippedDefinition(SkippedDefinitionContext Context); /// ActOnTagDefinitionError - Invoked when there was an unrecoverable diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 610207cf8b9a4..dd45aa9c8d5dc 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -4445,6 +4445,7 @@ unsigned FunctionDecl::getMemoryFunctionKind() const { case Builtin::BImempcpy: return Builtin::BImempcpy; + case Builtin::BI__builtin_trivially_relocate: case Builtin::BI__builtin_memmove: case Builtin::BI__builtin___memmove_chk: case Builtin::BImemmove: diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 7eff776882629..b80c8827ee839 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -103,13 +103,16 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D) HasConstexprDefaultConstructor(false), DefaultedDestructorIsConstexpr(true), HasNonLiteralTypeFieldsOrBases(false), StructuralIfLiteral(true), - UserProvidedDefaultConstructor(false), DeclaredSpecialMembers(0), + UserProvidedDefaultConstructor(false), UserProvidedMoveAssignment(false), + UserProvidedCopyAssignment(false), ExplicitlyDeletedMoveAssignment(false), + DeclaredSpecialMembers(0), ImplicitCopyConstructorCanHaveConstParamForVBase(true), ImplicitCopyConstructorCanHaveConstParamForNonVBase(true), ImplicitCopyAssignmentHasConstParam(true), HasDeclaredCopyConstructorWithConstParam(false), HasDeclaredCopyAssignmentWithConstParam(false), - IsAnyDestructorNoReturn(false), IsHLSLIntangible(false), IsLambda(false), + IsAnyDestructorNoReturn(false), IsHLSLIntangible(false), + IsTriviallyRelocatable(false), IsReplaceable(false), IsLambda(false), IsParsingBaseSpecifiers(false), ComputedVisibleConversions(false), HasODRHash(false), Definition(D) {} @@ -1529,7 +1532,10 @@ void CXXRecordDecl::addedEligibleSpecialMemberFunction(const CXXMethodDecl *MD, if (DD->isNoReturn()) data().IsAnyDestructorNoReturn = true; } - + if (SMKind == SMF_CopyAssignment) + data().UserProvidedCopyAssignment = MD->isUserProvided(); + else if (SMKind == SMF_MoveAssignment) + data().UserProvidedMoveAssignment = MD->isUserProvided(); if (!MD->isImplicit() && !MD->isUserProvided()) { // This method is user-declared but not user-provided. We can't work // out whether it's trivial yet (not until we get to the end of the @@ -1551,6 +1557,9 @@ void CXXRecordDecl::addedEligibleSpecialMemberFunction(const CXXMethodDecl *MD, if (!MD->isUserProvided()) data().DeclaredNonTrivialSpecialMembersForCall |= SMKind; } + + if (MD->isDeleted() && SMKind == SMF_MoveAssignment) + data().ExplicitlyDeletedMoveAssignment = true; } void CXXRecordDecl::finishedDefaultedOrDeletedMember(CXXMethodDecl *D) { @@ -1578,8 +1587,11 @@ void CXXRecordDecl::finishedDefaultedOrDeletedMember(CXXMethodDecl *D) { data().HasIrrelevantDestructor = false; } else if (D->isCopyAssignmentOperator()) SMKind |= SMF_CopyAssignment; - else if (D->isMoveAssignmentOperator()) + else if (D->isMoveAssignmentOperator()) { SMKind |= SMF_MoveAssignment; + if (!D->isIneligibleOrNotSelected() && D->isDeleted()) + data().ExplicitlyDeletedMoveAssignment = true; + } // Update which trivial / non-trivial special members we have. // addedMember will have skipped this step for this member. diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 8c11ec2e1fe24..77700056d8952 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -2862,6 +2862,30 @@ bool QualType::isTriviallyRelocatableType(const ASTContext &Context) const { } } +bool QualType::isCppTriviallyRelocatableType(const ASTContext &Context) const { + QualType BaseElementType = Context.getBaseElementType(*this); + if (BaseElementType->isIncompleteType()) + return false; + else if (BaseElementType->isScalarType()) + return true; + else if (const auto *RD = BaseElementType->getAsCXXRecordDecl()) + return RD->isTriviallyRelocatable(); + return false; +} + +bool QualType::isReplaceableType(const ASTContext &Context) const { + if (isConstQualified()) + return false; + QualType BaseElementType = Context.getBaseElementType(getUnqualifiedType()); + if (BaseElementType->isIncompleteType()) + return false; + if (BaseElementType->isScalarType()) + return true; + if (const auto *RD = BaseElementType->getAsCXXRecordDecl()) + return RD->isReplaceable(); + return false; +} + bool QualType::isNonWeakInMRRWithObjCWeak(const ASTContext &Context) const { return !Context.getLangOpts().ObjCAutoRefCount && Context.getLangOpts().ObjCWeak && diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index d57f491a20c8e..8e7e714960321 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -4762,6 +4762,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, return RValue::get(Dest, *this); } + case Builtin::BI__builtin_trivially_relocate: case Builtin::BImemmove: case Builtin::BI__builtin_memmove: { Address Dest = EmitPointerWithAlignment(E->getArg(0)); diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp index 77833f5d1defb..48140a6375eb0 100644 --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -757,7 +757,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts, Builder.defineMacro("__cpp_explicit_this_parameter", "202110L"); } - // We provide those C++23 features as extensions in earlier language modes, so + // We provide those C++2b features as extensions in earlier language modes, so // we also define their feature test macros. if (LangOpts.CPlusPlus11) Builder.defineMacro("__cpp_static_call_operator", "202207L"); @@ -768,6 +768,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts, Builder.defineMacro("__cpp_pack_indexing", "202311L"); Builder.defineMacro("__cpp_deleted_function", "202403L"); Builder.defineMacro("__cpp_variadic_friend", "202403L"); + Builder.defineMacro("__cpp_trivial_relocatability", "202502L"); if (LangOpts.Char8) Builder.defineMacro("__cpp_char8_t", "202207L"); diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 43db715ac6d70..3ed94fdf02012 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -2027,7 +2027,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, (DSC != DeclSpecContext::DSC_association && getLangOpts().CPlusPlus && Tok.is(tok::colon)) || (isClassCompatibleKeyword() && - (NextToken().is(tok::l_brace) || NextToken().is(tok::colon)))) { + (NextToken().is(tok::l_brace) || NextToken().is(tok::colon) || + isClassCompatibleKeyword(NextToken())))) { if (DS.isFriendSpecified()) { // C++ [class.friend]p2: // A class shall not be defined in a friend declaration. @@ -2046,15 +2047,15 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, (NextToken().is(tok::l_square) || NextToken().is(tok::kw_alignas) || NextToken().isRegularKeywordAttribute() || - isCXX11VirtSpecifier(NextToken()) != VirtSpecifiers::VS_None)) { + isCXX11VirtSpecifier(NextToken()) != VirtSpecifiers::VS_None || + isCXX2CTriviallyRelocatableKeyword())) { // We can't tell if this is a definition or reference // until we skipped the 'final' and C++11 attribute specifiers. TentativeParsingAction PA(*this); // Skip the 'final', abstract'... keywords. - while (isClassCompatibleKeyword()) { + while (isClassCompatibleKeyword()) ConsumeToken(); - } // Skip C++11 attribute specifiers. while (true) { @@ -2692,16 +2693,63 @@ bool Parser::isCXX11FinalKeyword() const { Specifier == VirtSpecifiers::VS_Sealed; } +bool Parser::isCXX2CTriviallyRelocatableKeyword(Token Tok) const { + if (!getLangOpts().CPlusPlus || Tok.isNot(tok::identifier)) + return false; + if (!Ident_trivially_relocatable_if_eligible) + Ident_trivially_relocatable_if_eligible = + &PP.getIdentifierTable().get("trivially_relocatable_if_eligible"); + IdentifierInfo *II = Tok.getIdentifierInfo(); + return II == Ident_trivially_relocatable_if_eligible; +} + +bool Parser::isCXX2CTriviallyRelocatableKeyword() const { + return isCXX2CTriviallyRelocatableKeyword(Tok); +} + +void Parser::ParseOptionalCXX2CTriviallyRelocatableSpecifier( + TriviallyRelocatableSpecifier &TRS) { + assert(isCXX2CTriviallyRelocatableKeyword() && + "expected a trivially_relocatable specifier"); + TRS = Actions.ActOnTriviallyRelocatableSpecifier(ConsumeToken()); +} + +bool Parser::isCXX2CReplaceableKeyword(Token Tok) const { + if (!getLangOpts().CPlusPlus || Tok.isNot(tok::identifier)) + return false; + if (!Ident_replaceable_if_eligible) + Ident_replaceable_if_eligible = + &PP.getIdentifierTable().get("replaceable_if_eligible"); + IdentifierInfo *II = Tok.getIdentifierInfo(); + return II == Ident_replaceable_if_eligible; +} + +bool Parser::isCXX2CReplaceableKeyword() const { + return isCXX2CReplaceableKeyword(Tok); +} + +void Parser::ParseOptionalCXX2CReplaceableSpecifier(ReplaceableSpecifier &MRS) { + assert(isCXX2CReplaceableKeyword() && + "expected a replaceable_if_eligible specifier"); + MRS = Actions.ActOnReplaceableSpecifier(ConsumeToken()); +} + /// isClassCompatibleKeyword - Determine whether the next token is a C++11 /// 'final' or Microsoft 'sealed' or 'abstract' contextual keywords. -bool Parser::isClassCompatibleKeyword() const { - VirtSpecifiers::Specifier Specifier = isCXX11VirtSpecifier(); +bool Parser::isClassCompatibleKeyword(Token Tok) const { + if (isCXX2CTriviallyRelocatableKeyword(Tok) || isCXX2CReplaceableKeyword(Tok)) + return true; + VirtSpecifiers::Specifier Specifier = isCXX11VirtSpecifier(Tok); return Specifier == VirtSpecifiers::VS_Final || Specifier == VirtSpecifiers::VS_GNU_Final || Specifier == VirtSpecifiers::VS_Sealed || Specifier == VirtSpecifiers::VS_Abstract; } +bool Parser::isClassCompatibleKeyword() const { + return isClassCompatibleKeyword(Tok); +} + /// Parse a C++ member-declarator up to, but not including, the optional /// brace-or-equal-initializer or pure-specifier. bool Parser::ParseCXXMemberDeclaratorBeforeInitializer( @@ -3580,21 +3628,19 @@ void Parser::SkipCXXMemberSpecification(SourceLocation RecordLoc, SourceLocation AttrFixitLoc, unsigned TagType, Decl *TagDecl) { // Skip the optional 'final' keyword. - if (getLangOpts().CPlusPlus && Tok.is(tok::identifier)) { - assert(isCXX11FinalKeyword() && "not a class definition"); + while (isClassCompatibleKeyword()) ConsumeToken(); - // Diagnose any C++11 attributes after 'final' keyword. - // We deliberately discard these attributes. - ParsedAttributes Attrs(AttrFactory); - CheckMisplacedCXX11Attribute(Attrs, AttrFixitLoc); + // Diagnose any C++11 attributes after 'final' keyword. + // We deliberately discard these attributes. + ParsedAttributes Attrs(AttrFactory); + CheckMisplacedCXX11Attribute(Attrs, AttrFixitLoc); - // This can only happen if we had malformed misplaced attributes; - // we only get called if there is a colon or left-brace after the - // attributes. - if (Tok.isNot(tok::colon) && Tok.isNot(tok::l_brace)) - return; - } + // This can only happen if we had malformed misplaced attributes; + // we only get called if there is a colon or left-brace after the + // attributes. + if (Tok.isNot(tok::colon) && Tok.isNot(tok::l_brace)) + return; // Skip the base clauses. This requires actually parsing them, because // otherwise we can't be sure where they end (a left brace may appear @@ -3808,13 +3854,39 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc, SourceLocation AbstractLoc; bool IsFinalSpelledSealed = false; bool IsAbstract = false; + TriviallyRelocatableSpecifier TriviallyRelocatable; + ReplaceableSpecifier Replacable; // Parse the optional 'final' keyword. if (getLangOpts().CPlusPlus && Tok.is(tok::identifier)) { while (true) { VirtSpecifiers::Specifier Specifier = isCXX11VirtSpecifier(Tok); - if (Specifier == VirtSpecifiers::VS_None) - break; + if (Specifier == VirtSpecifiers::VS_None) { + if (isCXX2CTriviallyRelocatableKeyword(Tok)) { + if (TriviallyRelocatable.isSet()) { + auto Skipped = Tok; + ConsumeToken(); + Diag(Skipped, diag::err_duplicate_class_relocation_specifier) + << 0 << TriviallyRelocatable.getLocation(); + } else { + ParseOptionalCXX2CTriviallyRelocatableSpecifier( + TriviallyRelocatable); + } + continue; + } else if (isCXX2CReplaceableKeyword(Tok)) { + if (Replacable.isSet()) { + auto Skipped = Tok; + ConsumeToken(); + Diag(Skipped, diag::err_duplicate_class_relocation_specifier) + << 1 << Replacable.getLocation(); + } else { + ParseOptionalCXX2CReplaceableSpecifier(Replacable); + } + continue; + } else { + break; + } + } if (isCXX11FinalKeyword()) { if (FinalLoc.isValid()) { auto Skipped = ConsumeToken(); @@ -3850,7 +3922,8 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc, else if (Specifier == VirtSpecifiers::VS_GNU_Final) Diag(FinalLoc, diag::ext_warn_gnu_final); } - assert((FinalLoc.isValid() || AbstractLoc.isValid()) && + assert((FinalLoc.isValid() || AbstractLoc.isValid() || + TriviallyRelocatable.isSet() || Replacable.isSet()) && "not a class definition"); // Parse any C++11 attributes after 'final' keyword. @@ -3923,9 +3996,9 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc, T.consumeOpen(); if (TagDecl) - Actions.ActOnStartCXXMemberDeclarations(getCurScope(), TagDecl, FinalLoc, - IsFinalSpelledSealed, IsAbstract, - T.getOpenLocation()); + Actions.ActOnStartCXXMemberDeclarations( + getCurScope(), TagDecl, FinalLoc, IsFinalSpelledSealed, IsAbstract, + TriviallyRelocatable, Replacable, T.getOpenLocation()); // C++ 11p3: Members of a class defined with the keyword class are private // by default. Members of a class defined with the keywords struct or union diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 0710542f5e938..11e4c2913c93c 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -513,6 +513,8 @@ void Parser::Initialize() { Ident_sealed = nullptr; Ident_abstract = nullptr; Ident_override = nullptr; + Ident_trivially_relocatable_if_eligible = nullptr; + Ident_replaceable_if_eligible = nullptr; Ident_GNU_final = nullptr; Ident_import = nullptr; Ident_module = nullptr; diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index aae61f612a4bc..0d28a5eeaa969 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -1875,6 +1875,54 @@ static ExprResult BuiltinIsWithinLifetime(Sema &S, CallExpr *TheCall) { << 0; return ExprError(); } + return TheCall; +} + +static ExprResult BuiltinTriviallyRelocate(Sema &S, CallExpr *TheCall) { + if (S.checkArgCount(TheCall, 3)) + return ExprError(); + + QualType ArgTy = TheCall->getArg(0)->getType(); + if (!ArgTy->isPointerType() || ArgTy.getCVRQualifiers() != 0) { + S.Diag(TheCall->getArg(0)->getExprLoc(), + diag::err_builtin_trivially_relocate_invalid_arg_type) + << /*a pointer*/ 0; + return ExprError(); + } + + QualType T = ArgTy->getPointeeType(); + if (S.RequireCompleteType(TheCall->getBeginLoc(), T, + diag::err_incomplete_type)) + return ExprError(); + + if (T.isConstQualified() || + !T.isCppTriviallyRelocatableType(S.getASTContext())) { + S.Diag(TheCall->getArg(0)->getExprLoc(), + diag::err_builtin_trivially_relocate_invalid_arg_type) + << (T.isConstQualified() ? /*non-const*/ 1 : /*relocatable*/ 2); + return ExprError(); + } + + TheCall->setType(ArgTy); + + QualType Dest = TheCall->getArg(1)->getType(); + if (Dest.getCanonicalType() != ArgTy.getCanonicalType()) { + S.Diag(TheCall->getArg(0)->getExprLoc(), + diag::err_builtin_trivially_relocate_invalid_arg_type) + << /*the same*/ 3; + return ExprError(); + } + + Expr *SizeExpr = TheCall->getArg(2); + ExprResult Size = S.DefaultLvalueConversion(SizeExpr); + if (Size.isInvalid()) + return ExprError(); + + Size = S.tryConvertExprToType(Size.get(), S.getASTContext().getSizeType()); + if (Size.isInvalid()) + return ExprError(); + SizeExpr = Size.get(); + TheCall->setArg(2, SizeExpr); return TheCall; } @@ -2317,6 +2365,9 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, return BuiltinLaunder(*this, TheCall); case Builtin::BI__builtin_is_within_lifetime: return BuiltinIsWithinLifetime(*this, TheCall); + case Builtin::BI__builtin_trivially_relocate: + return BuiltinTriviallyRelocate(*this, TheCall); + case Builtin::BI__sync_fetch_and_add: case Builtin::BI__sync_fetch_and_add_1: case Builtin::BI__sync_fetch_and_add_2: diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 362df485a025c..9a7e40879e241 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -18307,11 +18307,19 @@ bool Sema::ActOnDuplicateDefinition(Decl *Prev, SkipBodyInfo &SkipBody) { return true; } -void Sema::ActOnStartCXXMemberDeclarations(Scope *S, Decl *TagD, - SourceLocation FinalLoc, - bool IsFinalSpelledSealed, - bool IsAbstract, - SourceLocation LBraceLoc) { +TriviallyRelocatableSpecifier +Sema::ActOnTriviallyRelocatableSpecifier(SourceLocation Loc) { + return {Loc}; +} + +ReplaceableSpecifier Sema::ActOnReplaceableSpecifier(SourceLocation Loc) { + return {Loc}; +} + +void Sema::ActOnStartCXXMemberDeclarations( + Scope *S, Decl *TagD, SourceLocation FinalLoc, bool IsFinalSpelledSealed, + bool IsAbstract, TriviallyRelocatableSpecifier TriviallyRelocatable, + ReplaceableSpecifier Replaceable, SourceLocation LBraceLoc) { AdjustDeclIfTemplate(TagD); CXXRecordDecl *Record = cast<CXXRecordDecl>(TagD); @@ -18329,6 +18337,13 @@ void Sema::ActOnStartCXXMemberDeclarations(Scope *S, Decl *TagD, ? FinalAttr::Keyword_sealed : FinalAttr::Keyword_final)); } + + if (TriviallyRelocatable.isSet() && !Record->isInvalidDecl()) + Record->setTriviallyRelocatableSpecifier(TriviallyRelocatable); + + if (Replaceable.isSet() && !Record->isInvalidDecl()) + Record->setReplaceableSpecifier(Replaceable); + // C++ [class]p2: // [...] The class-name is also inserted into the scope of the // class itself; this is known as the injected-class-name. For diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 664d48ccbc382..8214ea137b6e0 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -7221,6 +7221,9 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) { checkClassLevelDLLAttribute(Record); checkClassLevelCodeSegAttribute(Record); + CheckCXX2CTriviallyRelocatable(Record); + CheckCXX2CReplaceable(Record); + bool ClangABICompat4 = Context.getLangOpts().getClangABICompat() <= LangOptions::ClangABI::Ver4; TargetInfo::CallingConvKind CCK = @@ -7258,6 +7261,217 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) { } } +static bool hasSuitableConstructorForReplaceability(CXXRecordDecl *D, + bool Implicit) { + assert(D->hasDefinition() && !D->isInvalidDecl()); + + bool HasDeletedMoveConstructor = false; + bool HasDeletedCopyConstructor = false; + bool HasMoveConstructor = D->needsImplicitMoveConstructor(); + bool HasDefaultedMoveConstructor = D->needsImplicitMoveConstructor(); + bool HasDefaultedCopyConstructor = D->needsImplicitMoveConstructor(); + + for (const Decl *D : D->decls()) { + auto *MD = dyn_cast<CXXConstructorDecl>(D); + if (!MD || MD->isIneligibleOrNotSelected()) + continue; + + if (MD->isMoveConstructor()) { + HasMoveConstructor = true; + if (MD->isDefaulted()) + HasDefaultedMoveConstructor = true; + if (MD->isDeleted()) + HasDeletedMoveConstructor = true; + } + if (MD->isCopyConstructor()) { + if (MD->isDefaulted()) + HasDefaultedCopyConstructor = true; + if (MD->isDeleted()) + HasDeletedCopyConstructor = true; + } + } + + if (HasMoveConstructor) + return !HasDeletedMoveConstructor && + (Implicit ? HasDefaultedMoveConstructor : true); + return !HasDeletedCopyConstructor && + (Implicit ? HasDefaultedCopyConstructor : true); + ; +} + +static bool hasSuitableMoveAssignmentOperatorForReplaceability(CXXRecordDecl *D, + bool Implicit) { + assert(D->hasDefinition() && !D->isInvalidDecl()); + + if (D->hasExplicitlyDeletedMoveAssignment()) + return false; + + bool HasDeletedMoveAssignment = false; + bool HasDeletedCopyAssignment = false; + bool HasMoveAssignment = D->needsImplicitMoveAssignment(); + bool HasDefaultedMoveAssignment = D->needsImplicitMoveAssignment(); + bool HasDefaultedCopyAssignment = D->needsImplicitCopyAssignment(); + + for (const Decl *D : D->decls()) { + auto *MD = dyn_cast<CXXMethodDecl>(D); + if (!MD || MD->isIneligibleOrNotSelected()) + continue; + + if (MD->isMoveAssignmentOperator()) { + HasMoveAssignment = true; + if (MD->isDefaulted()) + HasDefaultedMoveAssignment = true; + if (MD->isDeleted()) + HasDeletedMoveAssignment = true; + } + if (MD->isCopyAssignmentOperator()) { + if (MD->isDefaulted()) + HasDefaultedCopyAssignment = true; + if (MD->isDeleted()) + HasDeletedCopyAssignment = true; + } + } + + if (HasMoveAssignment) + return !HasDeletedMoveAssignment && + (Implicit ? HasDefaultedMoveAssignment : true); + return !HasDeletedCopyAssignment && + (Implicit ? HasDefaultedCopyAssignment : true); +} + +void Sema::CheckCXX2CTriviallyRelocatable(CXXRecordDecl *D) { + if (!D->hasDefinition() || D->isInvalidDecl()) + return; + + bool MarkedTriviallyRelocatable = + D->getTriviallyRelocatableSpecifier().isSet(); + + bool IsTriviallyRelocatable = true; + for (const CXXBaseSpecifier &B : D->bases()) { + const auto *BaseDecl = B.getType()->getAsCXXRecordDecl(); + if (!BaseDecl) + continue; + if (B.isVirtual() || + (!BaseDecl->isDependentType() && !BaseDecl->isTriviallyRelocatable())) { + IsTriviallyRelocatable = false; + } + } + + for (const FieldDecl *Field : D->fields()) { + if (Field->getType()->isDependentType()) + continue; + if (Field->getType()->isReferenceType()) + continue; + QualType T = getASTContext().getBaseElementType( + Field->getType().getUnqualifiedType()); + if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) { + if (RD->isTriviallyRelocatable()) + continue; + IsTriviallyRelocatable = false; + break; + } + } + + if (!D->isDependentType() && !MarkedTriviallyRelocatable) { + bool HasSuitableMoveCtr = D->needsImplicitMoveConstructor(); + bool HasSuitableCopyCtr = false; + if (D->hasUserDeclaredDestructor()) { + const auto *Dtr = D->getDestructor(); + if (Dtr && (!Dtr->isDefaulted() || Dtr->isDeleted())) + IsTriviallyRelocatable = false; + } + if (IsTriviallyRelocatable && !HasSuitableMoveCtr) { + for (const CXXConstructorDecl *CD : D->ctors()) { + if (CD->isMoveConstructor() && CD->isDefaulted() && + !CD->isIneligibleOrNotSelected()) { + HasSuitableMoveCtr = true; + break; + } + } + } + if (!HasSuitableMoveCtr && !D->hasMoveConstructor()) { + HasSuitableCopyCtr = D->needsImplicitCopyConstructor(); + if (!HasSuitableCopyCtr) { + for (const CXXConstructorDecl *CD : D->ctors()) { + if (CD->isCopyConstructor() && CD->isDefaulted() && + !CD->isIneligibleOrNotSelected()) { + HasSuitableCopyCtr = true; + break; + } + } + } + } + + if (D->isUnion() && !D->hasUserDeclaredCopyConstructor() && + !D->hasUserDeclaredCopyAssignment() && + !D->hasUserDeclaredMoveOperation() && !D->hasUserDeclaredDestructor()) { + // Do nothing + } + + else if (!HasSuitableMoveCtr && !HasSuitableCopyCtr) + IsTriviallyRelocatable = false; + + else if (IsTriviallyRelocatable && + ((!D->needsImplicitMoveAssignment() && + (D->hasUserProvidedMoveAssignment() || + D->hasExplicitlyDeletedMoveAssignment())) || + (!D->hasMoveAssignment() && + D->hasUserProvidedCopyAssignment()))) { + IsTriviallyRelocatable = false; + } + } + + D->setIsTriviallyRelocatable(IsTriviallyRelocatable); +} + +void Sema::CheckCXX2CReplaceable(CXXRecordDecl *D) { + if (!D->hasDefinition() || D->isInvalidDecl()) + return; + + bool MarkedCXX2CReplaceable = D->getReplaceableSpecifier().isSet(); + + bool IsReplaceable = true; + + for (const CXXBaseSpecifier &B : D->bases()) { + const auto *BaseDecl = B.getType()->getAsCXXRecordDecl(); + if (!BaseDecl) + continue; + if ((!BaseDecl->isDependentType() && !BaseDecl->isReplaceable()) || + B.isVirtual()) + IsReplaceable = false; + } + + if (!hasSuitableConstructorForReplaceability(D, !MarkedCXX2CReplaceable) || + !hasSuitableMoveAssignmentOperatorForReplaceability( + D, !MarkedCXX2CReplaceable)) { + IsReplaceable = false; + } + + if (IsReplaceable) { + for (const FieldDecl *Field : D->fields()) { + if (Field->getType()->isDependentType()) + continue; + if (Field->getType()->isReferenceType()) { + IsReplaceable = false; + break; + } + if (Field->getType().isConstQualified()) { + IsReplaceable = false; + break; + } + QualType T = getASTContext().getBaseElementType( + Field->getType().getUnqualifiedType()); + if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) { + if (RD->isReplaceable()) + continue; + IsReplaceable = false; + } + } + } + + D->setIsReplaceable(IsReplaceable); +} + /// Look up the special member function that would be called by a special /// member function for a subobject of class type. /// diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 34219e0235a74..379f2456e08f3 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -5107,6 +5107,8 @@ static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, TypeTrait UTT, // impose the same constraints. case UTT_IsTriviallyRelocatable: case UTT_IsTriviallyEqualityComparable: + case UTT_IsCppTriviallyRelocatable: + case UTT_IsReplaceable: case UTT_CanPassInRegs: // Per the GCC type traits documentation, T shall be a complete type, cv void, // or an array of unknown bound. But GCC actually imposes the same constraints @@ -5678,6 +5680,10 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT, return T.isTriviallyRelocatableType(C); case UTT_IsBitwiseCloneable: return T.isBitwiseCloneableType(C); + case UTT_IsCppTriviallyRelocatable: + return T.isCppTriviallyRelocatableType(C); + case UTT_IsReplaceable: + return T.isReplaceableType(C); case UTT_CanPassInRegs: if (CXXRecordDecl *RD = T->getAsCXXRecordDecl(); RD && !T.hasQualifiers()) return RD->canPassInRegisters(); diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 121da4916ed43..136a7ff39d5bb 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -3689,6 +3689,10 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation, // Start the definition of this instantiation. Instantiation->startDefinition(); + Instantiation->setTriviallyRelocatableSpecifier( + Pattern->getTriviallyRelocatableSpecifier()); + Instantiation->setReplaceableSpecifier(Pattern->getReplaceableSpecifier()); + // The instantiation is visible here, even if it was first declared in an // unimported module. Instantiation->setVisibleDespiteOwningModule(); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits