diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 168bdca3c880b..960660b18b87a 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -2491,6 +2491,8 @@ class ASTContext : public RefCountedBase { TypeInfoChars getTypeInfoInChars(const Type *T) const; TypeInfoChars getTypeInfoInChars(QualType T) const; + CharUnits getStartOfValueRepresentation(QualType T) const; + /// Determine if the alignment the type has was required using an /// alignment attribute. bool isAlignmentRequired(const Type *T) const; 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 252e6e9256414..98379b392bb0b 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -127,6 +127,28 @@ class AccessSpecDecl : public Decl { static bool classofKind(Kind K) { return K == AccessSpec; } }; +enum class MemberwiseRelocatableOrReplaceableKind { Relocatable, Replaceable }; + +template +class BasicMemberwiseSpecifier { +public: + BasicMemberwiseSpecifier() = default; + BasicMemberwiseSpecifier(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 = BasicMemberwiseSpecifier< + MemberwiseRelocatableOrReplaceableKind::Relocatable>; +using MemberwiseReplaceableSpecifier = BasicMemberwiseSpecifier< + MemberwiseRelocatableOrReplaceableKind::Replaceable>; + /// Represents a base class of a C++ class. /// /// Each CXXBaseSpecifier represents a single, direct base class (or @@ -349,6 +371,10 @@ class CXXRecordDecl : public RecordDecl { /// This is actually currently stored in reverse order. LazyDeclPtr FirstFriend; + TriviallyRelocatableSpecifier TriviallyRelocatable; + + MemberwiseReplaceableSpecifier MemberwiseReplaceable; + DefinitionData(CXXRecordDecl *D); /// Retrieve the set of direct base classes. @@ -716,12 +742,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 +839,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 { @@ -1464,6 +1509,24 @@ class CXXRecordDecl : public RecordDecl { return isLiteral() && data().StructuralIfLiteral; } + TriviallyRelocatableSpecifier getTriviallyRelocatableSpecifier() const { + return data().TriviallyRelocatable; + } + + MemberwiseReplaceableSpecifier getMemberwiseReplaceableSpecifier() const { + return data().MemberwiseReplaceable; + } + + 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 @@ -1898,6 +1961,13 @@ class CXXRecordDecl : public RecordDecl { return K >= firstCXXRecord && K <= lastCXXRecord; } void markAbstract() { data().Abstract = true; } + + void setTriviallyRelocatableSpecifier(TriviallyRelocatableSpecifier TRS) { + data().TriviallyRelocatable = TRS; + } + void setMemberwiseReplaceableSpecifier(MemberwiseReplaceableSpecifier MRS) { + data().MemberwiseReplaceable = MRS; + } }; /// Store information needed for an explicit specifier. diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index f1a2aac0a8b2f..af39843dae3d4 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -530,7 +530,7 @@ class alignas(void *) Stmt { unsigned : NumExprBits; LLVM_PREFERRED_TYPE(UnaryExprOrTypeTrait) - unsigned Kind : 3; + unsigned Kind : 4; LLVM_PREFERRED_TYPE(bool) unsigned IsType : 1; // true if operand is a type, false if an expression. }; diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index ef36a73716454..641311a52093a 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -1142,6 +1142,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 92118418d9d45..2bd8a7bf1de5b 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -2756,6 +2756,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 0aa2c4a70849a..fa61b218a22af 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1049,6 +1049,9 @@ def err_access_specifier_interface : Error< def err_duplicate_class_virt_specifier : Error< "class already marked '%0'">; +def err_duplicate_class_memberwise_specifier : Error< + "class already marked %select{'memberwise_trivially_relocatable'|'memberwise_replaceable'}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 58819a64813fc..29eeb40d5f9de 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -84,7 +84,8 @@ def err_typecheck_converted_constant_expression_indirect : Error< def err_expr_not_cce : Error< "%select{case value|enumerator value|non-type template argument|" "array size|explicit specifier argument|noexcept specifier argument|" - "call to 'size()'|call to 'data()'}0 is not a constant expression">; + "call to 'size()'|call to 'data()'|" + "trivially_relocatable specifier argument}0 is not a constant expression">; def ext_cce_narrowing : ExtWarn< "%select{case value|enumerator value|non-type template argument|" "array size|explicit specifier argument|noexcept specifier argument|" @@ -2680,6 +2681,21 @@ def warn_final_dtor_non_final_class : Warning< def note_final_dtor_non_final_class_silence : Note< "mark %0 as '%select{final|sealed}1' to silence this warning">; +def warn_trivially_relocatable_specifier_on_non_relocatable_class : Warning< + "invalid 'trivially_relocatable' specifier on non trivially-relocatable class %0">, + InGroup>, + DefaultError; + +def warn_memberwise_replaceable_specifier_on_non_relocatable_class : Warning< + "invalid 'memberwise_replaceable' specifier on non memberwise replaceable class %0">, + InGroup>, + DefaultError; + +def note_trivially_relocatable: Note< + "because it %select{inherits from a non trivially-relocatable class %1|" + "has a virtual base class %1|" + "has a deleted move constructor or assignment operator}0">; + // C++11 attributes def err_repeat_attribute : Error<"%0 attribute cannot be repeated">; diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index a82ff684b2ac7..dcf80fb32889c 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -320,6 +320,7 @@ KEYWORD(short , KEYALL) KEYWORD(signed , KEYALL) UNARY_EXPR_OR_TYPE_TRAIT(sizeof, SizeOf, KEYALL) UNARY_EXPR_OR_TYPE_TRAIT(__datasizeof, DataSizeOf, KEYCXX) +UNARY_EXPR_OR_TYPE_TRAIT(__value_representation_begin, ValueRepresentationBegin, KEYCXX) KEYWORD(static , KEYALL) KEYWORD(struct , KEYALL) KEYWORD(switch , KEYALL) @@ -557,6 +558,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 47f72135c97cf..53433da608260 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -13,6 +13,7 @@ #ifndef LLVM_CLANG_PARSE_PARSER_H #define LLVM_CLANG_PARSE_PARSER_H +#include "clang/AST/DeclCXX.h" #include "clang/Basic/OpenACCKinds.h" #include "clang/Basic/OperatorPrecedence.h" #include "clang/Lex/CodeCompletionHandler.h" @@ -164,6 +165,8 @@ class Parser : public CodeCompletionHandler { mutable IdentifierInfo *Ident_final; mutable IdentifierInfo *Ident_GNU_final; mutable IdentifierInfo *Ident_override; + mutable IdentifierInfo *Ident_trivially_relocatable; + mutable IdentifierInfo *Ident_memberwise_replaceable; // C++2a contextual keywords. mutable IdentifierInfo *Ident_import; @@ -3164,6 +3167,20 @@ class Parser : public CodeCompletionHandler { SourceLocation FriendLoc); bool isCXX11FinalKeyword() const; + + bool isCXX2CTriviallyRelocatableKeyword(Token Tok) const; + bool isCXX2CTriviallyRelocatableKeyword() const; + bool SkipCXX2CTriviallyRelocatableSpecifier(); + void ParseOptionalCXX2CTriviallyRelocatableSpecifier( + TriviallyRelocatableSpecifier &TRS); + + bool isCXX2CMemberwiseReplaceableKeyword(Token Tok) const; + bool isCXX2CMemberwiseReplaceableKeyword() const; + void ParseOptionalCXX2CMemberwiseReplaceableSpecifier( + MemberwiseReplaceableSpecifier &MRS); + bool SkipCXX2CMemberwiseReplaceableSpecifier(); + + 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 68c782a15c6f1..eccbe0baa8bd0 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -20,6 +20,8 @@ #include "clang/AST/AttrIterator.h" #include "clang/AST/CharUnits.h" #include "clang/AST/DeclBase.h" +#include "clang/AST/Availability.h" +#include "clang/AST/ComparisonCategories.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclarationName.h" @@ -3983,20 +3985,31 @@ 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); + + MemberwiseReplaceableSpecifier + ActOnMemberwiseReplaceableSpecifier(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, + MemberwiseReplaceableSpecifier MemberwiseReplaceable, + 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 CheckMemberwiseReplaceable(CXXRecordDecl *D); + void ActOnTagFinishSkippedDefinition(SkippedDefinitionContext Context); /// ActOnTagDefinitionError - Invoked when there was an unrecoverable @@ -10039,6 +10052,8 @@ class Sema final : public SemaBase { ///< message. CCEK_StaticAssertMessageData, ///< Call to data() in a static assert ///< message. + /// + CCEK_TriviallyRelocatable, }; ExprResult BuildConvertedConstantExpression(Expr *From, QualType T, diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index fa9cc38efc466..b206ce94d84de 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -1796,6 +1796,19 @@ TypeInfoChars ASTContext::getTypeInfoDataSizeInChars(QualType T) const { return Info; } +CharUnits ASTContext::getStartOfValueRepresentation(QualType T) const { + if (getLangOpts().CPlusPlus) { + if (const auto *RT = T->getAs(); + RT && !RT->getDecl()->isInvalidDecl()) { + const ASTRecordLayout &layout = getASTRecordLayout(RT->getDecl()); + if (layout.getFieldCount()) + return toCharUnitsFromBits(layout.getFieldOffset(0)); + return layout.getDataSize(); + } + } + return CharUnits::Zero(); +} + /// getConstantArrayInfoInChars - Performing the computation in CharUnits /// instead of in bits prevents overflowing the uint64_t for some large arrays. TypeInfoChars diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 43268bafc387b..b00fe70688946 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -1780,7 +1780,8 @@ bool Compiler::VisitUnaryExprOrTypeTraitExpr( UnaryExprOrTypeTrait Kind = E->getKind(); const ASTContext &ASTCtx = Ctx.getASTContext(); - if (Kind == UETT_SizeOf || Kind == UETT_DataSizeOf) { + if (Kind == UETT_SizeOf || Kind == UETT_DataSizeOf || + Kind == UETT_ValueRepresentationBegin) { QualType ArgType = E->getTypeOfArgument(); // C++ [expr.sizeof]p2: "When applied to a reference or a reference type, @@ -1797,8 +1798,10 @@ bool Compiler::VisitUnaryExprOrTypeTraitExpr( if (Kind == UETT_SizeOf) Size = ASTCtx.getTypeSizeInChars(ArgType); - else + else if (Kind == UETT_DataSizeOf) Size = ASTCtx.getTypeInfoDataSizeInChars(ArgType).Width; + else + Size = ASTCtx.getStartOfValueRepresentation(ArgType); } if (DiscardResult) diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index a14b1b33d35ef..d9c9d43cb47c2 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -4414,6 +4414,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 01143391edab4..ce7e3e5354894 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) {} @@ -893,15 +896,15 @@ void CXXRecordDecl::addedMember(Decl *D) { if (Method->isCopyAssignmentOperator()) { SMKind |= SMF_CopyAssignment; - const auto *ParamTy = Method->getNonObjectParameter(0)->getType()->getAs(); if (!ParamTy || ParamTy->getPointeeType().isConstQualified()) data().HasDeclaredCopyAssignmentWithConstParam = true; } - if (Method->isMoveAssignmentOperator()) + if (Method->isMoveAssignmentOperator()) { SMKind |= SMF_MoveAssignment; + } // Keep the list of conversion functions up-to-date. if (auto *Conversion = dyn_cast(D)) { @@ -1494,7 +1497,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 @@ -1516,6 +1522,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) { @@ -1543,8 +1552,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/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 78d2500636004..10df142c798b4 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -14289,6 +14289,17 @@ bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr( } return Success(Sizeof, E); } + case UETT_ValueRepresentationBegin: { + QualType SrcTy = E->getTypeOfArgument(); + if (const ReferenceType *Ref = SrcTy->getAs()) + SrcTy = Ref->getPointeeType(); + if (SrcTy->isDependentType() || SrcTy->isIncompleteType()) { + Info.FFDiag(E->getBeginLoc()); + return false; + } + return Success(Info.Ctx.getStartOfValueRepresentation(SrcTy), E); + } + case UETT_OpenMPRequiredSimdAlign: assert(E->isArgumentType()); return Success( diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 7d638befcbd3f..c5661eb8f6871 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -5215,6 +5215,14 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, Diags.Report(DiagID); return; } + case UETT_ValueRepresentationBegin: { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID( + DiagnosticsEngine::Error, + "cannot yet mangle __value_representation_begin expression"); + Diags.Report(DiagID); + return; + } case UETT_PtrAuthTypeDiscriminator: { DiagnosticsEngine &Diags = Context.getDiags(); unsigned DiagID = Diags.getCustomDiagID( diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index a55e6c8bf0261..7a8b53a6a05ed 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -2819,6 +2819,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 da7a1a55da531..032b08a2d41dc 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -4277,6 +4277,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/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 88fbbe6c4c965..818a7eac01687 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -3355,8 +3355,9 @@ Value * ScalarExprEmitter::VisitUnaryExprOrTypeTraitExpr( const UnaryExprOrTypeTraitExpr *E) { QualType TypeToSize = E->getTypeOfArgument(); - if (auto Kind = E->getKind(); - Kind == UETT_SizeOf || Kind == UETT_DataSizeOf) { + if (auto Kind = E->getKind(); Kind == UETT_SizeOf || + Kind == UETT_DataSizeOf || + Kind == clang::UETT_ValueRepresentationBegin) { if (const VariableArrayType *VAT = CGF.getContext().getAsVariableArrayType(TypeToSize)) { if (E->isArgumentType()) { diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp index 9a0fdb175ff29..e2323106c1726 100644 --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -754,7 +754,10 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts, Builder.defineMacro("__cpp_auto_cast", "202110L"); } - // We provide those C++23 features as extensions in earlier language modes, so + // Experimental + Builder.defineMacro("__cpp_trivial_relocatability", "202302L"); + + // 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"); diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 6370da1fab004..1d08bcb669064 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -11,12 +11,14 @@ //===----------------------------------------------------------------------===// #include "clang/AST/ASTContext.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/PrettyDeclStackTrace.h" #include "clang/Basic/AttributeCommonInfo.h" #include "clang/Basic/Attributes.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/OperatorKinds.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/TokenKinds.h" #include "clang/Lex/LiteralSupport.h" @@ -2026,7 +2028,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. @@ -2045,14 +2048,19 @@ 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()) { - ConsumeToken(); + if (isCXX2CTriviallyRelocatableKeyword()) { + if (!SkipCXX2CTriviallyRelocatableSpecifier()) + break; + } else + ConsumeToken(); } // Skip C++11 attribute specifiers. @@ -2691,16 +2699,77 @@ 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) + Ident_trivially_relocatable = + &PP.getIdentifierTable().get("memberwise_trivially_relocatable"); + IdentifierInfo *II = Tok.getIdentifierInfo(); + return II == Ident_trivially_relocatable; +} + +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::SkipCXX2CTriviallyRelocatableSpecifier() { + assert(isCXX2CTriviallyRelocatableKeyword() && + "expected a trivially_relocatable specifier"); + ConsumeToken(); + return true; +} + +bool Parser::isCXX2CMemberwiseReplaceableKeyword(Token Tok) const { + if (!getLangOpts().CPlusPlus || Tok.isNot(tok::identifier)) + return false; + if (!Ident_memberwise_replaceable) + Ident_memberwise_replaceable = + &PP.getIdentifierTable().get("memberwise_replaceable"); + IdentifierInfo *II = Tok.getIdentifierInfo(); + return II == Ident_memberwise_replaceable; +} + +bool Parser::isCXX2CMemberwiseReplaceableKeyword() const { + return isCXX2CMemberwiseReplaceableKeyword(Tok); +} +void Parser::ParseOptionalCXX2CMemberwiseReplaceableSpecifier( + MemberwiseReplaceableSpecifier &MRS) { + assert(isCXX2CMemberwiseReplaceableKeyword() && + "expected a memberwise_replacable specifier"); + MRS = Actions.ActOnMemberwiseReplaceableSpecifier(ConsumeToken()); +} +bool Parser::SkipCXX2CMemberwiseReplaceableSpecifier() { + assert(isCXX2CMemberwiseReplaceableKeyword() && + "expected a memberwise_replacable specifier"); + ConsumeToken(); + return true; +} + /// 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) || + isCXX2CMemberwiseReplaceableKeyword(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( @@ -3577,21 +3646,24 @@ 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"); - ConsumeToken(); + while (isClassCompatibleKeyword()) { + if (isCXX2CTriviallyRelocatableKeyword()) { + if (!SkipCXX2CTriviallyRelocatableSpecifier()) + return; + } else + 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 @@ -3805,13 +3877,40 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc, SourceLocation AbstractLoc; bool IsFinalSpelledSealed = false; bool IsAbstract = false; + TriviallyRelocatableSpecifier TriviallyRelocatable; + MemberwiseReplaceableSpecifier MemberwiseReplacable; // 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; + SkipCXX2CTriviallyRelocatableSpecifier(); + Diag(Skipped, diag::err_duplicate_class_memberwise_specifier) + << 0 << TriviallyRelocatable.getLocation(); + } else { + ParseOptionalCXX2CTriviallyRelocatableSpecifier( + TriviallyRelocatable); + } + continue; + } else if (isCXX2CMemberwiseReplaceableKeyword(Tok)) { + if (MemberwiseReplacable.isSet()) { + auto Skipped = Tok; + SkipCXX2CMemberwiseReplaceableSpecifier(); + Diag(Skipped, diag::err_duplicate_class_memberwise_specifier) + << 1 << MemberwiseReplacable.getLocation(); + } else { + ParseOptionalCXX2CMemberwiseReplaceableSpecifier( + MemberwiseReplacable); + } + continue; + } else { + break; + } + } if (isCXX11FinalKeyword()) { if (FinalLoc.isValid()) { auto Skipped = ConsumeToken(); @@ -3847,7 +3946,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() || MemberwiseReplacable.isSet()) && "not a class definition"); // Parse any C++11 attributes after 'final' keyword. @@ -3920,9 +4020,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, MemberwiseReplacable, 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/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 64f284d78b24d..430e68fc973fa 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1526,6 +1526,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, // unary-expression: '__datasizeof' unary-expression // unary-expression: '__datasizeof' '(' type-name ')' case tok::kw___datasizeof: + case tok::kw___value_representation_begin: case tok::kw_vec_step: // unary-expression: OpenCL 'vec_step' expression // unary-expression: '__builtin_omp_required_simd_align' '(' type-name ')' case tok::kw___builtin_omp_required_simd_align: @@ -2437,8 +2438,9 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok, SourceRange &CastRange) { assert(OpTok.isOneOf(tok::kw_typeof, tok::kw_typeof_unqual, tok::kw_sizeof, - tok::kw___datasizeof, tok::kw___alignof, tok::kw_alignof, - tok::kw__Alignof, tok::kw_vec_step, + tok::kw___datasizeof, + tok::kw___value_representation_begin, tok::kw___alignof, + tok::kw_alignof, tok::kw__Alignof, tok::kw_vec_step, tok::kw___builtin_omp_required_simd_align, tok::kw___builtin_vectorelements) && "Not a typeof/sizeof/alignof/vec_step expression!"); @@ -2449,7 +2451,8 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok, if (Tok.isNot(tok::l_paren)) { // If construct allows a form without parenthesis, user may forget to put // pathenthesis around type name. - if (OpTok.isOneOf(tok::kw_sizeof, tok::kw___datasizeof, tok::kw___alignof, + if (OpTok.isOneOf(tok::kw_sizeof, tok::kw___datasizeof, + tok::kw___value_representation_begin, tok::kw___alignof, tok::kw_alignof, tok::kw__Alignof)) { if (isTypeIdUnambiguously()) { DeclSpec DS(AttrFactory); @@ -2486,7 +2489,8 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok, // followed by a non-parenthesized expression, BalancedDelimiterTracker // is not going to help when the nesting is too deep. In this corner case // we continue to parse with sufficient stack space to avoid crashing. - if (OpTok.isOneOf(tok::kw_sizeof, tok::kw___datasizeof, tok::kw___alignof, + if (OpTok.isOneOf(tok::kw_sizeof, tok::kw___datasizeof, + tok::kw___value_representation_begin, tok::kw___alignof, tok::kw_alignof, tok::kw__Alignof) && Tok.isOneOf(tok::kw_sizeof, tok::kw___datasizeof, tok::kw___alignof, tok::kw_alignof, tok::kw__Alignof)) @@ -2573,7 +2577,8 @@ ExprResult Parser::ParseSYCLUniqueStableNameExpression() { /// [C++11] 'alignof' '(' type-id ')' /// \endverbatim ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() { - assert(Tok.isOneOf(tok::kw_sizeof, tok::kw___datasizeof, tok::kw___alignof, + assert(Tok.isOneOf(tok::kw_sizeof, tok::kw___datasizeof, + tok::kw___value_representation_begin, tok::kw___alignof, tok::kw_alignof, tok::kw__Alignof, tok::kw_vec_step, tok::kw___builtin_omp_required_simd_align, tok::kw___builtin_vectorelements) && @@ -2664,6 +2669,9 @@ ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() { case tok::kw___datasizeof: ExprKind = UETT_DataSizeOf; break; + case tok::kw___value_representation_begin: + ExprKind = UETT_ValueRepresentationBegin; + break; case tok::kw___builtin_vectorelements: ExprKind = UETT_VectorElements; break; diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 04c2f1d380bc4..715ee8b2a1737 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 = nullptr; + Ident_memberwise_replaceable = 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 99500daca295c..6a97c99179935 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -38,6 +38,7 @@ #include "clang/AST/TypeLoc.h" #include "clang/AST/UnresolvedSet.h" #include "clang/Basic/AddressSpaces.h" +#include "clang/Basic/Builtins.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/IdentifierTable.h" @@ -1878,6 +1879,42 @@ 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) + return ExprError(); // Todo : Diag + + QualType T = ArgTy->getPointeeType(); + if (S.RequireCompleteType(TheCall->getBeginLoc(), T, + diag::err_incomplete_type)) + return ExprError(); + + if (T.isConstQualified() || + !T.isCppTriviallyRelocatableType(S.getASTContext())) + return ExprError(); + + TheCall->setType(ArgTy); + + QualType Dest = TheCall->getArg(1)->getType(); + if (Dest.getCanonicalType() != ArgTy.getCanonicalType()) + 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; } @@ -2316,6 +2353,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 0f63c764536ec..cfada5097f00f 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -32,6 +32,7 @@ #include "clang/Basic/Builtins.h" #include "clang/Basic/HLSLRuntime.h" #include "clang/Basic/PartialDiagnostic.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/Lex/HeaderSearch.h" // TODO: Sema shouldn't depend on Lex @@ -18026,11 +18027,21 @@ 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}; +} + +MemberwiseReplaceableSpecifier +Sema::ActOnMemberwiseReplaceableSpecifier(SourceLocation Loc) { + return {Loc}; +} + +void Sema::ActOnStartCXXMemberDeclarations( + Scope *S, Decl *TagD, SourceLocation FinalLoc, bool IsFinalSpelledSealed, + bool IsAbstract, TriviallyRelocatableSpecifier TriviallyRelocatable, + MemberwiseReplaceableSpecifier MemberwiseReplaceable, + SourceLocation LBraceLoc) { AdjustDeclIfTemplate(TagD); CXXRecordDecl *Record = cast(TagD); @@ -18048,6 +18059,13 @@ void Sema::ActOnStartCXXMemberDeclarations(Scope *S, Decl *TagD, ? FinalAttr::Keyword_sealed : FinalAttr::Keyword_final)); } + + if (TriviallyRelocatable.isSet() && !Record->isInvalidDecl()) + Record->setTriviallyRelocatableSpecifier(TriviallyRelocatable); + + if (MemberwiseReplaceable.isSet() && !Record->isInvalidDecl()) + Record->setMemberwiseReplaceableSpecifier(MemberwiseReplaceable); + // 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 6b6fa98bf394c..b933e1a8e5c7f 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17,6 +17,7 @@ #include "clang/AST/CXXInheritance.h" #include "clang/AST/CharUnits.h" #include "clang/AST/ComparisonCategories.h" +#include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/EvaluatedExprVisitor.h" @@ -53,6 +54,7 @@ #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/SaveAndRestore.h" #include @@ -7155,6 +7157,9 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) { checkClassLevelDLLAttribute(Record); checkClassLevelCodeSegAttribute(Record); + CheckCXX2CTriviallyRelocatable(Record); + CheckMemberwiseReplaceable(Record); + bool ClangABICompat4 = Context.getLangOpts().getClangABICompat() <= LangOptions::ClangABI::Ver4; TargetInfo::CallingConvKind CCK = @@ -7192,6 +7197,244 @@ 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(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(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; + + SourceLocation Loc = D->getTriviallyRelocatableSpecifier().getLocation(); + bool MarkedTriviallyRelocatable = + D->getTriviallyRelocatableSpecifier().isSet(); + auto DiagnosticInvalidExplicitSpecifier = [&, NotedError = false]() mutable { + if (NotedError) + return; + Diag(Loc, + diag::warn_trivially_relocatable_specifier_on_non_relocatable_class) + << D; + NotedError = true; + }; + 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())) { + if (MarkedTriviallyRelocatable) { + DiagnosticInvalidExplicitSpecifier(); + Diag(B.getBeginLoc(), diag::note_trivially_relocatable) + << (B.isVirtual() ? 1 : 0) << BaseDecl << B.getSourceRange(); + } + 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::CheckMemberwiseReplaceable(CXXRecordDecl *D) { + if (!D->hasDefinition() || D->isInvalidDecl()) + return; + + SourceLocation Loc = D->getMemberwiseReplaceableSpecifier().getLocation(); + bool MarkedMemberwiseReplaceable = + D->getMemberwiseReplaceableSpecifier().isSet(); + auto DiagnosticInvalidExplicitSpecifier = [&, NotedError = false]() mutable { + if (NotedError) + return false; + Diag(Loc, + diag::warn_memberwise_replaceable_specifier_on_non_relocatable_class) + << D; + bool Old = NotedError; + NotedError = true; + return !Old; + }; + 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()) { + if (MarkedMemberwiseReplaceable) { + if (DiagnosticInvalidExplicitSpecifier()) + Diag(B.getBeginLoc(), diag::note_trivially_relocatable) + << (B.isVirtual()? 1 : 0) << BaseDecl << B.getSourceRange(); + } + IsReplaceable = false; + } + } + + if(!hasSuitableConstructorForReplaceability(D, !MarkedMemberwiseReplaceable) + || !hasSuitableMoveAssignmentOperatorForReplaceability(D, !MarkedMemberwiseReplaceable)) { + IsReplaceable = false; + if (MarkedMemberwiseReplaceable) { + if (DiagnosticInvalidExplicitSpecifier()) + Diag(D->getBeginLoc(), diag::note_trivially_relocatable) + << 2 << D->getSourceRange(); + } + } + + 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/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 32dac4440fb82..d7c6fe032eb20 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -4216,8 +4216,8 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(Expr *E, bool IsUnevaluatedOperand = (ExprKind == UETT_SizeOf || ExprKind == UETT_DataSizeOf || - ExprKind == UETT_AlignOf || ExprKind == UETT_PreferredAlignOf || - ExprKind == UETT_VecStep); + ExprKind == UETT_ValueRepresentationBegin || ExprKind == UETT_AlignOf || + ExprKind == UETT_PreferredAlignOf || ExprKind == UETT_VecStep); if (IsUnevaluatedOperand) { ExprResult Result = CheckUnevaluatedOperand(E); if (Result.isInvalid()) diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 14feafd1e6b17..f56ad8e51b55d 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -5084,6 +5084,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 @@ -5654,6 +5656,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_IsReferenceable: return T.isReferenceable(); case UTT_CanPassInRegs: diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 861b0a91240b3..9052e89024181 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -6084,7 +6084,8 @@ static ExprResult BuildConvertedConstantExpression(Sema &S, Expr *From, // expression is a constant expression and the implicit conversion // sequence contains only [... list of conversions ...]. ImplicitConversionSequence ICS = - (CCE == Sema::CCEK_ExplicitBool || CCE == Sema::CCEK_Noexcept) + (CCE == Sema::CCEK_ExplicitBool || CCE == Sema::CCEK_Noexcept || + CCE == Sema::CCEK_TriviallyRelocatable) ? TryContextuallyConvertToBool(S, From) : TryCopyInitialization(S, From, T, /*SuppressUserConversions=*/false, diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index c42cc250bb904..90514df48b2bb 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -3428,6 +3428,34 @@ namespace clang { } } +static TriviallyRelocatableSpecifier InstantiateTriviallyRelocatableSpecifier( + Sema &S, const TriviallyRelocatableSpecifier &Pattern, + const MultiLevelTemplateArgumentList &) { + return Pattern; +} + +/// Instantiate the definition of a class from a given pattern. +/// +/// \param PointOfInstantiation The point of instantiation within the +/// source code. +/// +/// \param Instantiation is the declaration whose definition is being +/// instantiated. This will be either a class template specialization +/// or a member class of a class template specialization. +/// +/// \param Pattern is the pattern from which the instantiation +/// occurs. This will be either the declaration of a class template or +/// the declaration of a member class of a class template. +/// +/// \param TemplateArgs The template arguments to be substituted into +/// the pattern. +/// +/// \param TSK the kind of implicit or explicit instantiation to perform. +/// +/// \param Complain whether to complain if the class cannot be instantiated due +/// to the lack of a definition. +/// +/// \returns true if an error occurred, false otherwise. bool Sema::InstantiateClass(SourceLocation PointOfInstantiation, CXXRecordDecl *Instantiation, CXXRecordDecl *Pattern, @@ -3498,6 +3526,13 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation, // Start the definition of this instantiation. Instantiation->startDefinition(); + TriviallyRelocatableSpecifier TRS = InstantiateTriviallyRelocatableSpecifier( + *this, Pattern->getTriviallyRelocatableSpecifier(), TemplateArgs); + Instantiation->setTriviallyRelocatableSpecifier(TRS); + + Instantiation->setMemberwiseReplaceableSpecifier( + Pattern->getMemberwiseReplaceableSpecifier()); + // The instantiation is visible here, even if it was first declared in an // unimported module. Instantiation->setVisibleDespiteOwningModule(); diff --git a/clang/test/Parser/cxx2c-trivially-relocatable.cpp b/clang/test/Parser/cxx2c-trivially-relocatable.cpp new file mode 100644 index 0000000000000..0a2a9fee86ac8 --- /dev/null +++ b/clang/test/Parser/cxx2c-trivially-relocatable.cpp @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -std=c++2b -verify -fsyntax-only %s + + +class A memberwise_trivially_relocatable {}; +class E final memberwise_trivially_relocatable {}; +class G memberwise_trivially_relocatable final{}; +class I memberwise_trivially_relocatable memberwise_trivially_relocatable final {}; // expected-error {{class already marked 'memberwise_trivially_relocatable'}} +class memberwise_trivially_relocatable memberwise_trivially_relocatable {}; diff --git a/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp b/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp new file mode 100644 index 0000000000000..54878190cc81c --- /dev/null +++ b/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp @@ -0,0 +1,271 @@ +// RUN: %clang_cc1 -std=c++2c -verify %s + +class Trivial {}; +struct NonRelocatable { + ~NonRelocatable(); +}; +static NonRelocatable NonRelocatable_g; + +class A memberwise_trivially_relocatable {}; +class B memberwise_trivially_relocatable : Trivial{}; +class C memberwise_trivially_relocatable { + int a; + void* b; + int c[3]; + Trivial d[3]; + NonRelocatable& e = NonRelocatable_g; +}; +class D memberwise_trivially_relocatable : Trivial {}; +class E memberwise_trivially_relocatable : virtual Trivial {}; +// expected-error@-1 {{invalid 'trivially_relocatable' specifier on non trivially-relocatable class 'E'}} +// expected-note@-2 {{because it has a virtual base class 'Trivial'}} + +class F memberwise_trivially_relocatable : NonRelocatable {}; +// expected-error@-1 {{invalid 'trivially_relocatable' specifier on non trivially-relocatable class 'F'}} +// expected-note@-2 {{because it inherits from a non trivially-relocatable class 'NonRelocatable'}} + +class I memberwise_trivially_relocatable { + NonRelocatable a; + NonRelocatable b[1]; + const NonRelocatable c; + const NonRelocatable d[1]; +}; + +class J memberwise_trivially_relocatable: virtual Trivial, NonRelocatable { +// expected-error@-1 {{invalid 'trivially_relocatable' specifier on non trivially-relocatable class 'J'}} +// expected-note@-2 {{because it has a virtual base class 'Trivial'}} +// expected-note@-3 {{because it inherits from a non trivially-relocatable class 'NonRelocatable'}} + NonRelocatable a; +}; + +class G memberwise_trivially_relocatable { + G(G&&); +}; + +class H memberwise_trivially_relocatable { + ~H(); +}; + +struct Incomplete; // expected-note {{forward declaration of 'Incomplete'}} +static_assert(__is_cpp_trivially_relocatable(Incomplete)); // expected-error {{incomplete type 'Incomplete' used in type trait expression}} +static_assert(__is_cpp_trivially_relocatable(Trivial)); +static_assert(__is_cpp_trivially_relocatable(G)); +static_assert(__is_cpp_trivially_relocatable(H)); +static_assert(__is_cpp_trivially_relocatable(int)); +static_assert(__is_cpp_trivially_relocatable(void*)); +static_assert(!__is_cpp_trivially_relocatable(int&)); +static_assert(!__is_cpp_trivially_relocatable(Trivial&)); +static_assert(__is_cpp_trivially_relocatable(const Trivial)); +static_assert(__is_cpp_trivially_relocatable(Trivial[1])); + +struct UserDtr { + ~UserDtr(); +}; + +struct DefaultedDtr { + ~DefaultedDtr() = default; +}; +struct UserMoveWithDefaultCopy { + UserMoveWithDefaultCopy(UserMoveWithDefaultCopy&&); + UserMoveWithDefaultCopy(const UserMoveWithDefaultCopy&) = default; +}; + +struct UserMove{ + UserMove(UserMove&&); +}; + +struct UserMoveDefault{ + UserMoveDefault(UserMoveDefault&&) = default; +}; + +struct UserCopy{ + UserCopy(const UserCopy&); +}; + +struct UserCopyDefault{ + UserCopyDefault(const UserCopyDefault&) = default; +}; + + +struct UserDeletedMove{ + UserDeletedMove(UserDeletedMove&) = delete; + UserDeletedMove(const UserDeletedMove&) = default; +}; + +static_assert(!__is_cpp_trivially_relocatable(UserDtr)); +static_assert(__is_cpp_trivially_relocatable(DefaultedDtr)); +static_assert(!__is_cpp_trivially_relocatable(UserMoveWithDefaultCopy)); +static_assert(!__is_cpp_trivially_relocatable(UserMove)); +static_assert(!__is_cpp_trivially_relocatable(UserCopy)); +static_assert(__is_cpp_trivially_relocatable(UserMoveDefault)); +static_assert(__is_cpp_trivially_relocatable(UserCopyDefault)); +static_assert(__is_cpp_trivially_relocatable(UserDeletedMove)); + +template +class TestDependentErrors memberwise_trivially_relocatable : T {}; +// expected-error@-1 {{invalid 'trivially_relocatable' specifier on non trivially-relocatable class 'TestDependentErrors'}} +// expected-note@-2 {{because it inherits from a non trivially-relocatable class 'NonRelocatable'}} +TestDependentErrors Ok; +TestDependentErrors Err; // expected-note {{in instantiation of template class}} + +struct DeletedMove { + DeletedMove(DeletedMove&&) = delete; +}; +struct DeletedCopy { + DeletedCopy(const DeletedCopy&) = delete; +}; +struct DeletedMoveAssign { + DeletedMoveAssign& operator=(DeletedMoveAssign&&) = delete; +}; + +static_assert(!__is_cpp_trivially_relocatable(DeletedMove)); +static_assert(!__is_cpp_trivially_relocatable(DeletedCopy)); +static_assert(!__is_cpp_trivially_relocatable(DeletedMoveAssign)); + +union U { + G g; +}; +static_assert(!__is_trivially_copyable(U)); +static_assert(__is_cpp_trivially_relocatable(U)); + + +namespace replaceable { + +struct DeletedMove { + DeletedMove(DeletedMove&&) = delete; +}; +struct DeletedCopy { + DeletedCopy(const DeletedCopy&) = delete; +}; +struct DeletedMoveAssign { + DeletedMoveAssign& operator=(DeletedMoveAssign&&) = delete; +}; + +struct DefaultedMove { + DefaultedMove(DefaultedMove&&) = default; + DefaultedMove& operator=(DefaultedMove&&) = default; +}; +struct DefaultedCopy { + DefaultedCopy(const DefaultedCopy&) = default; + DefaultedCopy(DefaultedCopy&&) = default; + DefaultedCopy& operator=(DefaultedCopy&&) = default; +}; +struct DefaultedMoveAssign { + DefaultedMoveAssign(DefaultedMoveAssign&&) = default; + DefaultedMoveAssign& operator=(DefaultedMoveAssign&&) = default; +}; + +struct UserProvidedMove { + UserProvidedMove(UserProvidedMove&&){}; +}; +struct UserProvidedCopy { + UserProvidedCopy(const UserProvidedCopy&) {}; +}; +struct UserProvidedMoveAssign { + UserProvidedMoveAssign& operator=(const UserProvidedMoveAssign&){return *this;}; +}; + +struct Empty{}; +static_assert(__builtin_is_replaceable(Empty)); +struct S1 memberwise_replaceable{}; +static_assert(__builtin_is_replaceable(S1)); + +static_assert(__builtin_is_replaceable(DefaultedMove)); +static_assert(__builtin_is_replaceable(DefaultedCopy)); +static_assert(__builtin_is_replaceable(DefaultedMoveAssign)); + +static_assert(!__builtin_is_replaceable(DeletedMove)); +static_assert(!__builtin_is_replaceable(DeletedCopy)); +static_assert(!__builtin_is_replaceable(DeletedMoveAssign)); + +static_assert(!__builtin_is_replaceable(UserProvidedMove)); +static_assert(!__builtin_is_replaceable(UserProvidedCopy)); +static_assert(!__builtin_is_replaceable(UserProvidedMoveAssign)); + +using NotReplaceable = DeletedMove; + +template +struct S { + T t; +}; + +template +struct WithBase : T{}; + +template +struct WithVBase : virtual T{}; + +struct WithVirtual { + virtual ~WithVirtual() = default; + WithVirtual(WithVirtual&&) = default; + WithVirtual& operator=(WithVirtual&&) = default; +}; + +static_assert(__builtin_is_replaceable(S)); +static_assert(__builtin_is_replaceable(S)); +static_assert(!__builtin_is_replaceable(S)); +static_assert(!__builtin_is_replaceable(S)); +static_assert(!__builtin_is_replaceable(S)); +static_assert(__builtin_is_replaceable(S)); +static_assert(!__builtin_is_replaceable(S)); +static_assert(__builtin_is_replaceable(WithBase>)); +static_assert(!__builtin_is_replaceable(WithBase>)); +static_assert(!__builtin_is_replaceable(WithBase)); +static_assert(!__builtin_is_replaceable(WithVBase>)); +static_assert(!__builtin_is_replaceable(WithVBase>)); +static_assert(!__builtin_is_replaceable(WithVBase)); +static_assert(__builtin_is_replaceable(WithVirtual)); + + +struct U1 memberwise_replaceable { + ~U1() = delete; + U1(U1&&) = default; + U1& operator=(U1&&) = default; + +}; +static_assert(__builtin_is_replaceable(U1)); + +struct U2 memberwise_replaceable { +// expected-error@-1 {{invalid 'memberwise_replaceable' specifier on non memberwise replaceable class 'U2'}} +// expected-note@-2 {{because it has a deleted move constructor or assignment operator}} + U2(const U2&) = delete; +}; +static_assert(!__builtin_is_replaceable(U2)); + + +template +struct WithVBaseExplicit memberwise_replaceable : virtual T{}; +// expected-error@-1 {{invalid 'memberwise_replaceable' specifier on non memberwise replaceable class 'WithVBaseExplicit>'}} \ +// expected-note@-1 {{because it has a virtual base class 'S'}} +static_assert(__builtin_is_replaceable(WithVBaseExplicit>)); // expected-error {{failed}} \ + // expected-note {{requested here}} + +struct S42 memberwise_trivially_relocatable memberwise_replaceable { + S42(S42&&); + S42& operator=(S42&&) = default; +}; +struct S43 memberwise_trivially_relocatable memberwise_replaceable { + S43(S43&&) = default; + S43& operator=(S43&&); +}; + + +struct Copyable1Explicit memberwise_replaceable { + Copyable1Explicit(Copyable1Explicit const &) = default; +}; + +struct Copyable1 { + Copyable1(Copyable1 const &) = default; +}; + + +struct CopyAssign1Explicit memberwise_replaceable { + CopyAssign1Explicit & operator=(const CopyAssign1Explicit&) = default; +}; + +struct CopyAssign1 { + CopyAssign1 & operator=(CopyAssign1 const &) = default; +}; + + +} diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index a571832ab724d..1f5372ea83bdb 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -548,6 +548,7 @@ set(files __memory/ranges_construct_at.h __memory/ranges_uninitialized_algorithms.h __memory/raw_storage_iterator.h + __memory/relocate.h __memory/shared_ptr.h __memory/swap_allocator.h __memory/temp_value.h diff --git a/libcxx/include/__algorithm/swap_ranges.h b/libcxx/include/__algorithm/swap_ranges.h index 54b453b72360e..e3f62e371da9e 100644 --- a/libcxx/include/__algorithm/swap_ranges.h +++ b/libcxx/include/__algorithm/swap_ranges.h @@ -40,6 +40,48 @@ __swap_ranges(_ForwardIterator1 __first1, _Sentinel1 __last1, _ForwardIterator2 template _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair<_ForwardIterator1, _ForwardIterator2> __swap_ranges(_ForwardIterator1 __first1, _Sentinel1 __last1, _ForwardIterator2 __first2) { +#if _LIBCPP_STD_VER >= 26 + if ! consteval { + using _V1Type = iterator_traits<_ForwardIterator1>::value_type; + using _V2Type = iterator_traits<_ForwardIterator2>::value_type; + if constexpr(__libcpp_is_contiguous_iterator<_ForwardIterator1>::value && + __libcpp_is_contiguous_iterator<_ForwardIterator2>::value && + is_same_v<_V1Type, _V2Type> && is_replaceable_v<_V1Type>) { + size_t __distance = distance(__first1, __last1); + if (__distance == 1) { + using std::swap; + swap(*__first1, *__first2); + } + else if (__distance > 0) { + size_t __numBytes = __distance * sizeof(_V1Type); + byte *__aptr = reinterpret_cast (addressof(*__first1)); + byte *__bptr = reinterpret_cast (addressof(*__first2)); + + size_t __size = __numBytes < 512 ? __numBytes : 512; + size_t __chunk = __numBytes / __size; + size_t __rem = __numBytes % __size; + + char __buffer[__size]; + if (__chunk > 0) { + for (std::size_t __n = 0; __n < __chunk; __n++, __aptr += __size, __bptr += __size) { + __builtin_memcpy(__buffer, __aptr, __size); + __builtin_memcpy(__aptr, __bptr, __size); + __builtin_memcpy(__bptr, __buffer, __size); + } + } + + if (__rem > 0) { + __builtin_memcpy(__buffer, __aptr, __rem); + __builtin_memcpy(__aptr, __bptr, __rem); + __builtin_memcpy(__bptr, __buffer, __rem); + } + } + return pair<_ForwardIterator1, _ForwardIterator2>(__first1 + __distance, __first2 + __distance); + } + } +#endif + +// else not replaceable, not contiguous, or constexpr while (__first1 != __last1) { _IterOps<_AlgPolicy>::iter_swap(__first1, __first2); ++__first1; diff --git a/libcxx/include/__memory/relocate.h b/libcxx/include/__memory/relocate.h new file mode 100644 index 0000000000000..856c0d79db3e7 --- /dev/null +++ b/libcxx/include/__memory/relocate.h @@ -0,0 +1,29 @@ +#ifndef _LIBCPP___MEMORY_RELOCATE_H +#define _LIBCPP___MEMORY_RELOCATE_H + +#include <__config> +#include <__functional/operations.h> +#include <__type_traits/is_const.h> +#include <__type_traits/is_trivially_relocatable.h> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 20 + +template +_LIBCPP_EXPORTED_FROM_ABI _Tp* trivially_relocate(_Tp* __begin, _Tp* __end, _Tp* __new_location) noexcept { + static_assert(is_trivially_relocatable_v<_Tp> && !is_const_v<_Tp>); + (void) __builtin_trivially_relocate(__new_location, __begin, sizeof(_Tp) * (__end - __begin)); + return __new_location + (__end - __begin); +} + +#endif + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___MEMORY_RELOCATE_H diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h index 8ff87e28b3bb5..afd6f6a8fdda6 100644 --- a/libcxx/include/__memory/uninitialized_algorithms.h +++ b/libcxx/include/__memory/uninitialized_algorithms.h @@ -21,6 +21,7 @@ #include <__memory/allocator_traits.h> #include <__memory/construct_at.h> #include <__memory/pointer_traits.h> +#include <__memory/relocate.h> #include <__memory/voidify.h> #include <__type_traits/enable_if.h> #include <__type_traits/extent.h> @@ -38,6 +39,8 @@ #include <__utility/pair.h> #include +#include + #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header #endif @@ -536,6 +539,71 @@ class _AllocatorDestroyRangeReverse { _Iter& __last_; }; +#if _LIBCPP_STD_VER >= 20 + +template +constexpr +_Tp* relocate(_Tp* __begin, _Tp* __end, _Tp* __new_location) +{ + static_assert(is_trivially_relocatable_v<_Tp> || is_nothrow_move_constructible_v<_Tp>); +// When relocating to the same location, do nothing. + if (__begin == __new_location || __begin == __end) + return __new_location + (__end - __begin); + +// Then, if we are not evaluating at compile time and the type supports +// trivial relocation, delegate to `trivially_relocate`. + if ! consteval { + if constexpr (is_trivially_relocatable_v<_Tp>) + return std::trivially_relocate(__begin, __end, __new_location); + } + + if constexpr (is_move_constructible_v<_Tp>) { + // For nontrivial relocatable types or any time during constant + // evaluation, we must detect overlapping ranges and act accordingly, + // which can be done only if the type is movable. + if ! consteval { + // At run time, when there is no overlap, we can, using other Standard + // Library algorithms, do all moves at once followed by all destructions. + if (less{}(__end,__new_location) || less{}(__new_location + (__end-__begin), __begin)) { + _Tp* __result = uninitialized_move(__begin, __end, __new_location); + std::destroy(__begin,__end); + return __result; + } + } + + if (less{}(__new_location,__begin) || less{}(__end,__new_location)) { + // Any move to a lower address in memory or any nonoverlapping move can be + // done by iterating forward through the range. + _Tp* __next = __begin; + _Tp* __dest = __new_location; + while (__next != __end) { + ::new(__dest) _Tp(std::move(*__next)); + __next->~_Tp(); + ++__next; ++__dest; + } + } + else { + // When moving to a higher address that overlaps, we must go backward through + // the range. + _Tp* __next = __end; + _Tp* __dest = __new_location + (__end - __begin); + while (__next != __begin) { + --__next; --__dest; + ::new(__dest) _Tp(std::move(*__next)); + __next->~_Tp(); + } + } + return __new_location + (__end - __begin); + } + +// The only way to reach this point is during constant evaluation where type `T` +// is trivially relocatable but not move constructible. Such cases are not supported +// so we mark this branch as unreachable. + unreachable(); +} + +#endif + // Copy-construct [__first1, __last1) in [__first2, __first2 + N), where N is distance(__first1, __last1). // // The caller has to ensure that __first2 can hold at least N uninitialized elements. If an exception is thrown the @@ -615,11 +683,22 @@ struct __allocator_has_trivial_destroy, _Up> : true_type {}; // - is_nothrow_move_constructible<_Tp> // - is_copy_constructible<_Tp> // - __libcpp_is_trivially_relocatable<_Tp> +// - is_trivially_relocatable_v<_Tp> template _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __uninitialized_allocator_relocate(_Alloc& __alloc, _Tp* __first, _Tp* __last, _Tp* __result) { static_assert(__is_cpp17_move_insertable<_Alloc>::value, "The specified type does not meet the requirements of Cpp17MoveInsertable"); + +#if _LIBCPP_STD_VER >= 20 + if (!__libcpp_is_constant_evaluated()) { + if constexpr (is_trivially_relocatable_v<_Tp> || is_nothrow_move_constructible_v<_Tp>) { + (void) relocate(__first, __last, const_cast<__remove_const_t<_Tp>*>(__result)); + return ; + } + } +#endif + if (__libcpp_is_constant_evaluated() || !__libcpp_is_trivially_relocatable<_Tp>::value || !__allocator_has_trivial_move_construct<_Alloc, _Tp>::value || !__allocator_has_trivial_destroy<_Alloc, _Tp>::value) { diff --git a/libcxx/include/__type_traits/is_trivially_relocatable.h b/libcxx/include/__type_traits/is_trivially_relocatable.h index c0871731cc001..341f9fceb8e36 100644 --- a/libcxx/include/__type_traits/is_trivially_relocatable.h +++ b/libcxx/include/__type_traits/is_trivially_relocatable.h @@ -21,22 +21,26 @@ _LIBCPP_BEGIN_NAMESPACE_STD -// A type is trivially relocatable if a move construct + destroy of the original object is equivalent to -// `memcpy(dst, src, sizeof(T))`. - -#if __has_builtin(__is_trivially_relocatable) -template -struct __libcpp_is_trivially_relocatable : integral_constant {}; -#else template -struct __libcpp_is_trivially_relocatable : is_trivially_copyable<_Tp> {}; -#endif +struct __libcpp_is_trivially_relocatable : integral_constant {}; template struct __libcpp_is_trivially_relocatable<_Tp, __enable_if_t::value> > : true_type {}; +template +struct is_trivially_relocatable : integral_constant {}; + +template +inline constexpr bool is_trivially_relocatable_v = __is_cpp_trivially_relocatable(_Tp); + +template +struct is_replaceable : integral_constant {}; + +template +inline constexpr bool is_replaceable_v = __builtin_is_replaceable(_Tp); + _LIBCPP_END_NAMESPACE_STD #endif // _LIBCPP___TYPE_TRAITS_IS_TRIVIALLY_RELOCATABLE_H diff --git a/libcxx/include/__utility/swap.h b/libcxx/include/__utility/swap.h index ecfbdec75a2ae..ed6ad3e5eb22f 100644 --- a/libcxx/include/__utility/swap.h +++ b/libcxx/include/__utility/swap.h @@ -13,12 +13,16 @@ #include <__type_traits/enable_if.h> #include <__type_traits/is_assignable.h> #include <__type_traits/is_constructible.h> +#include <__type_traits/is_trivially_assignable.h> +#include <__type_traits/is_trivially_constructible.h> #include <__type_traits/is_nothrow_assignable.h> #include <__type_traits/is_nothrow_constructible.h> #include <__type_traits/is_swappable.h> +#include <__type_traits/is_trivially_relocatable.h> #include <__utility/declval.h> #include <__utility/move.h> #include +#include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -37,9 +41,53 @@ template using __swap_result_t = void; #endif +#if _LIBCPP_STD_VER >= 26 +template + requires std::is_trivially_relocatable_v<_Tp> && std::is_replaceable_v<_Tp> +void swap_value_representations(_Tp& __a, _Tp& __b) { + // For now we assume the value representation correspond to the memory + // between the start of the first subobject and the end of the last subobject + // which holds for both itanium and MSVC. + constexpr size_t __value_size = __datasizeof(_Tp) - __value_representation_begin(_Tp); + if constexpr (__value_size == 0) + return; + + char* __aptr = reinterpret_cast(&__a) + __value_representation_begin(_Tp); + char* __bptr = reinterpret_cast(&__b) + __value_representation_begin(_Tp); + + constexpr size_t __size = __value_size < 512 ? __datasizeof(_Tp) : 512; + constexpr size_t __chunk = __value_size / __size; + constexpr size_t __rem = __value_size % __size; + + // TODO: research better memswap algorithms + char __buffer[__size]; + if constexpr (__chunk) { + for (std::size_t n = 0; n < __chunk; n++, __aptr += __size, __bptr += __size) { + __builtin_memcpy(__buffer, __aptr, __size); + __builtin_memmove(__aptr, __bptr, __size); + __builtin_memmove(__bptr, __buffer, __size); + } + } + if constexpr (__rem) { + __builtin_memcpy(__buffer, __aptr, __rem); + __builtin_memmove(__aptr, __bptr, __rem); + __builtin_memmove(__bptr, __buffer, __rem); + } +} +#endif + template inline _LIBCPP_HIDE_FROM_ABI __swap_result_t<_Tp> _LIBCPP_CONSTEXPR_SINCE_CXX20 swap(_Tp& __x, _Tp& __y) _NOEXCEPT_(is_nothrow_move_constructible<_Tp>::value&& is_nothrow_move_assignable<_Tp>::value) { +#if _LIBCPP_STD_VER >= 26 + if !consteval { + if constexpr (!(std::is_trivially_move_assignable_v<_Tp> && std::is_trivially_move_constructible_v<_Tp>) && + std::is_trivially_relocatable_v<_Tp> && std::is_replaceable_v<_Tp>) { + swap_value_representations(__x, __y); + return; + } + } +#endif _Tp __t(std::move(__x)); __x = std::move(__y); __y = std::move(__t); diff --git a/libcxx/include/memory b/libcxx/include/memory index b940a32c3ebe6..d87149a663eb6 100644 --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -945,6 +945,7 @@ template #include <__memory/out_ptr.h> #include <__memory/pointer_traits.h> #include <__memory/raw_storage_iterator.h> +#include <__memory/relocate.h> #include <__memory/shared_ptr.h> #include <__memory/temporary_buffer.h> #include <__memory/uninitialized_algorithms.h> diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap index 65df579b8d6dd..9d15dc4764f7d 100644 --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -1504,6 +1504,7 @@ module std_private_memory_allocator_destructor [system] { header "__m module std_private_memory_allocator_traits [system] { header "__memory/allocator_traits.h" } module std_private_memory_assume_aligned [system] { header "__memory/assume_aligned.h" } module std_private_memory_auto_ptr [system] { header "__memory/auto_ptr.h" } +module std_private_memory_relocate [system] { header "__memory/relocate.h" } module std_private_memory_builtin_new_allocator [system] { header "__memory/builtin_new_allocator.h" export * @@ -2102,6 +2103,9 @@ module std_private_utility_small_buffer [system] { header "__utility/ module std_private_utility_swap [system] { header "__utility/swap.h" export std_private_type_traits_is_swappable + export std_private_type_traits_is_assignable + export std_private_type_traits_is_constructible + export std_private_concepts_movable } module std_private_utility_to_underlying [system] { header "__utility/to_underlying.h" } module std_private_utility_unreachable [system] { header "__utility/unreachable.h" } diff --git a/libcxx/include/string b/libcxx/include/string index 3480b57375c11..1fef17db545f6 100644 --- a/libcxx/include/string +++ b/libcxx/include/string @@ -750,7 +750,8 @@ struct __uninitialized_size_tag {}; struct __init_with_sentinel_tag {}; template -class basic_string { +class basic_string memberwise_trivially_relocatable { + private: using __default_allocator_type = allocator<_CharT>; diff --git a/libcxx/include/vector b/libcxx/include/vector index 2442852c764a6..002793484e356 100644 --- a/libcxx/include/vector +++ b/libcxx/include/vector @@ -336,6 +336,7 @@ template requires is-vector-bool-reference // Since C++ #include <__memory/allocator_traits.h> #include <__memory/noexcept_move_assign_container.h> #include <__memory/pointer_traits.h> +#include <__memory/relocate.h> #include <__memory/swap_allocator.h> #include <__memory/temp_value.h> #include <__memory/uninitialized_algorithms.h> @@ -1575,6 +1576,19 @@ vector<_Tp, _Allocator>::erase(const_iterator __position) { __position != end(), "vector::erase(iterator) called with a non-dereferenceable iterator"); difference_type __ps = __position - cbegin(); pointer __p = this->__begin_ + __ps; +#if _LIBCPP_STD_VER >= 20 + if constexpr (is_trivially_relocatable_v<_Tp> || is_nothrow_move_constructible_v<_Tp>) { + size_type __old_size = size(); + // destroy the element at __p + __alloc_traits::destroy(__alloc(), std::__to_address(__p)); + // move the rest down + (void) relocate(__p + 1, this->__end_, __p); + // update the end + this->__end_--; + __annotate_shrink(__old_size); + return __make_iter(__p); + } +#endif this->__destruct_at_end(std::move(__p + 1, this->__end_, __p)); return __make_iter(__p); } @@ -1585,6 +1599,22 @@ vector<_Tp, _Allocator>::erase(const_iterator __first, const_iterator __last) { _LIBCPP_ASSERT_VALID_INPUT_RANGE(__first <= __last, "vector::erase(first, last) called with invalid range"); pointer __p = this->__begin_ + (__first - begin()); if (__first != __last) { +#if _LIBCPP_STD_VER >= 20 + if constexpr (is_trivially_relocatable_v<_Tp> || is_nothrow_move_constructible_v<_Tp>) { + size_type __old_size = size(); + size_type __count = __last - __first; + pointer __p0 = __p + __count; + // destroy the elements at [__p, __p0) + for (pointer __to_destroy = __p; __to_destroy < __p0; ++__to_destroy) + __alloc_traits::destroy(__alloc(), std::__to_address(__to_destroy)); + // move the rest down + (void) relocate(std::__to_address(__p0), std::__to_address(this->__end_), std::__to_address(__p)); + // update the end + this->__end_ -= __count; + __annotate_shrink(__old_size); + return __make_iter(__p); + } +#endif this->__destruct_at_end(std::move(__p + (__last - __first), this->__end_, __p)); } return __make_iter(__p); @@ -1613,6 +1643,23 @@ vector<_Tp, _Allocator>::insert(const_iterator __position, const_reference __x) if (__p == this->__end_) { __construct_one_at_end(__x); } else { +#if _LIBCPP_STD_VER >= 20 + if constexpr (is_trivially_relocatable_v<_Tp> || is_nothrow_move_constructible_v<_Tp>) { + // Make space by trivially relocating everything + _ConstructTransaction __tx(*this, 1); + (void) relocate(std::__to_address(__p), std::__to_address(this->__end_), __p + 1); + // construct the new element (not assign!) + const_pointer __xr = pointer_traits::pointer_to(__x); + if (std::__is_pointer_in_range(std::__to_address(__p), std::__to_address(__end_), std::addressof(__x))) + ++__xr; + __alloc_traits::construct(this->__alloc(), std::__to_address(__p), *__xr); + ++__tx.__pos_; + // Need to fix up upon an exception! + // update all the invariants. + // return an iterator to the new entry + return __make_iter(__p); + } +#endif __move_range(__p, this->__end_, __p + 1); const_pointer __xr = pointer_traits::pointer_to(__x); if (std::__is_pointer_in_range(std::__to_address(__p), std::__to_address(__end_), std::addressof(__x))) @@ -1636,6 +1683,20 @@ vector<_Tp, _Allocator>::insert(const_iterator __position, value_type&& __x) { if (__p == this->__end_) { __construct_one_at_end(std::move(__x)); } else { +#if _LIBCPP_STD_VER >= 20 + if constexpr (is_trivially_relocatable_v<_Tp> || is_nothrow_move_constructible_v<_Tp>) { + // Make space by trivially relocating everything + _ConstructTransaction __tx(*this, 1); + (void) relocate(std::__to_address(__p), std::__to_address(this->__end_), __p + 1); + // construct the new element (not assign!) + __alloc_traits::construct(this->__alloc(), std::__to_address(__p), std::forward(__x)); + ++__tx.__pos_; + // Need to fix up upon an exception! + // update all the invariants. + // return an iterator to the new entry + return __make_iter(__p); + } +#endif __move_range(__p, this->__end_, __p + 1); *__p = std::move(__x); } @@ -1657,6 +1718,20 @@ vector<_Tp, _Allocator>::emplace(const_iterator __position, _Args&&... __args) { if (__p == this->__end_) { __construct_one_at_end(std::forward<_Args>(__args)...); } else { +#if _LIBCPP_STD_VER >= 20 + if constexpr (is_trivially_relocatable_v<_Tp> || is_nothrow_move_constructible_v<_Tp>) { + // Make space by trivially relocating everything + _ConstructTransaction __tx(*this, 1); + (void) relocate(std::__to_address(__p), std::__to_address(this->__end_), __p + 1); + // construct the new element + __alloc_traits::construct(this->__alloc(), std::__to_address(__p), std::forward<_Args>(__args)...); + ++__tx.__pos_; + // Need to fix up upon an exception! + // update all the invariants. + // return an iterator to the new entry + return __make_iter(__p); + } +#endif __temp_value __tmp(this->__alloc(), std::forward<_Args>(__args)...); __move_range(__p, this->__end_, __p + 1); *__p = std::move(__tmp.get());