[clang] [Clang][WIP] Trivial relocation (PR #127636)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Feb 20 08:09:24 PST 2025
https://github.com/cor3ntin updated https://github.com/llvm/llvm-project/pull/127636
>From 9d2fd12df5ad75bd4bc0e5c46821b820fb55cdbb Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 18 Feb 2025 14:37:32 +0100
Subject: [PATCH 1/4] [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 +
.../Parser/cxx2c-trivially-relocatable.cpp | 8 +
.../SemaCXX/cxx2c-trivially-relocatable.cpp | 257 ++++++++++++++++++
23 files changed, 835 insertions(+), 42 deletions(-)
create mode 100644 clang/test/Parser/cxx2c-trivially-relocatable.cpp
create mode 100644 clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
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();
diff --git a/clang/test/Parser/cxx2c-trivially-relocatable.cpp b/clang/test/Parser/cxx2c-trivially-relocatable.cpp
new file mode 100644
index 0000000000000..7a575124a3b35
--- /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 trivially_relocatable_if_eligible {};
+class E final trivially_relocatable_if_eligible {};
+class G trivially_relocatable_if_eligible final{};
+class I trivially_relocatable_if_eligible trivially_relocatable_if_eligible final {}; // expected-error {{class already marked 'trivially_relocatable_if_eligible'}}
+class trivially_relocatable_if_eligible trivially_relocatable_if_eligible {};
diff --git a/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp b/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
new file mode 100644
index 0000000000000..7d300dfa4847c
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
@@ -0,0 +1,257 @@
+// RUN: %clang_cc1 -std=c++2c -verify %s
+
+class Trivial {};
+struct NonRelocatable {
+ ~NonRelocatable();
+};
+static NonRelocatable NonRelocatable_g;
+
+class A trivially_relocatable_if_eligible {};
+class B trivially_relocatable_if_eligible : Trivial{};
+class C trivially_relocatable_if_eligible {
+ int a;
+ void* b;
+ int c[3];
+ Trivial d[3];
+ NonRelocatable& e = NonRelocatable_g;
+};
+class D trivially_relocatable_if_eligible : Trivial {};
+class E trivially_relocatable_if_eligible : virtual Trivial {};
+
+class F trivially_relocatable_if_eligible : NonRelocatable {};
+
+class I trivially_relocatable_if_eligible {
+ NonRelocatable a;
+ NonRelocatable b[1];
+ const NonRelocatable c;
+ const NonRelocatable d[1];
+};
+
+class J trivially_relocatable_if_eligible: virtual Trivial, NonRelocatable {
+ NonRelocatable a;
+};
+
+class G trivially_relocatable_if_eligible {
+ G(G&&);
+};
+
+class H trivially_relocatable_if_eligible {
+ ~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 <typename T>
+class TestDependentErrors trivially_relocatable_if_eligible : T {};
+TestDependentErrors<Trivial> Ok;
+TestDependentErrors<NonRelocatable> Err;
+
+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 replaceable_if_eligible{};
+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 <typename T>
+struct S {
+ T t;
+};
+
+template <typename T>
+struct WithBase : T{};
+
+template <typename T>
+struct WithVBase : virtual T{};
+
+struct WithVirtual {
+ virtual ~WithVirtual() = default;
+ WithVirtual(WithVirtual&&) = default;
+ WithVirtual& operator=(WithVirtual&&) = default;
+};
+
+static_assert(__builtin_is_replaceable(S<int>));
+static_assert(__builtin_is_replaceable(S<volatile int>));
+static_assert(!__builtin_is_replaceable(S<const int>));
+static_assert(!__builtin_is_replaceable(S<const int&>));
+static_assert(!__builtin_is_replaceable(S<int&>));
+static_assert(__builtin_is_replaceable(S<int[2]>));
+static_assert(!__builtin_is_replaceable(S<const int[2]>));
+static_assert(__builtin_is_replaceable(WithBase<S<int>>));
+static_assert(!__builtin_is_replaceable(WithBase<S<const int>>));
+static_assert(!__builtin_is_replaceable(WithBase<UserProvidedMove>));
+static_assert(!__builtin_is_replaceable(WithVBase<S<int>>));
+static_assert(!__builtin_is_replaceable(WithVBase<S<const int>>));
+static_assert(!__builtin_is_replaceable(WithVBase<UserProvidedMove>));
+static_assert(__builtin_is_replaceable(WithVirtual));
+
+
+struct U1 replaceable_if_eligible {
+ ~U1() = delete;
+ U1(U1&&) = default;
+ U1& operator=(U1&&) = default;
+
+};
+static_assert(__builtin_is_replaceable(U1));
+
+struct U2 replaceable_if_eligible {
+ U2(const U2&) = delete;
+};
+static_assert(!__builtin_is_replaceable(U2));
+
+
+template <typename T>
+struct WithVBaseExplicit replaceable_if_eligible : virtual T{};
+static_assert(__builtin_is_replaceable(WithVBaseExplicit<S<int>>)); // expected-error {{failed}}
+
+struct S42 trivially_relocatable_if_eligible replaceable_if_eligible {
+ S42(S42&&);
+ S42& operator=(S42&&) = default;
+};
+struct S43 trivially_relocatable_if_eligible replaceable_if_eligible {
+ S43(S43&&) = default;
+ S43& operator=(S43&&);
+};
+
+
+struct Copyable1Explicit replaceable_if_eligible {
+ Copyable1Explicit(Copyable1Explicit const &) = default;
+};
+
+struct Copyable1 {
+ Copyable1(Copyable1 const &) = default;
+};
+
+
+struct CopyAssign1Explicit replaceable_if_eligible {
+ CopyAssign1Explicit & operator=(const CopyAssign1Explicit&) = default;
+};
+
+struct CopyAssign1 {
+ CopyAssign1 & operator=(CopyAssign1 const &) = default;
+};
+
+
+}
>From 80e37354c14f19a3a53f60cc1184988011898b48 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Thu, 20 Feb 2025 16:43:55 +0100
Subject: [PATCH 2/4] add sema tests for __builtin_trivially_relocate
---
clang/include/clang/Basic/DiagnosticSemaKinds.td | 5 +++--
.../test/SemaCXX/cxx2c-trivially-relocatable.cpp | 15 +++++++++++++++
2 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index be2a68411ae1a..2292afe5f9ecb 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12428,8 +12428,9 @@ def err_builtin_invalid_arg_type: Error <
"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">;
+ "first%select{||| and second}0 argument%select{|||s}0 to "
+ "'__builtin_trivially_relocate' must be"
+ " %select{a pointer|non-const|relocatable|of the same type}0">;
def err_builtin_matrix_disabled: Error<
"matrix types extension is disabled. Pass -fenable-matrix to enable it">;
diff --git a/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp b/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
index 7d300dfa4847c..1ca641226a292 100644
--- a/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
+++ b/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
@@ -253,5 +253,20 @@ struct CopyAssign1 {
CopyAssign1 & operator=(CopyAssign1 const &) = default;
};
+}
+
+
+void test__builtin_trivially_relocate() {
+ struct S{ ~S();};
+ struct R {};
+ __builtin_trivially_relocate(); //expected-error {{too few arguments to function call, expected 3, have 0}}
+ __builtin_trivially_relocate(0, 0, 0, 0); //expected-error {{too many arguments to function call, expected 3, have 4}}
+ __builtin_trivially_relocate(0, 0, 0); //expected-error {{argument to '__builtin_trivially_relocate' must be a pointer}}
+ __builtin_trivially_relocate((const int*)0, 0, 0); //expected-error {{argument to '__builtin_trivially_relocate' must be non-const}}
+ __builtin_trivially_relocate((S*)0, 0, 0); //expected-error {{argument to '__builtin_trivially_relocate' must be relocatable}}
+ __builtin_trivially_relocate((int*)0, 0, 0); //expected-error {{first and second arguments to '__builtin_trivially_relocate' must be of the same type}}
+ __builtin_trivially_relocate((int*)0, (int*)0, (int*)0); // expected-error{{cannot initialize a value of type 'unsigned long' with an rvalue of type 'int *'}}
+ __builtin_trivially_relocate((int*)0, (int*)0, 0);
+ __builtin_trivially_relocate((R*)0, (R*)0, 0);
}
>From c4c47f650c569839183c28c0bc2c0839d9cf480c Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Thu, 20 Feb 2025 16:53:56 +0100
Subject: [PATCH 3/4] rename __is_cpp_trivially_relocatable
---
clang/include/clang/Basic/TokenKinds.def | 2 +-
.../SemaCXX/cxx2c-trivially-relocatable.cpp | 44 +++++++++----------
2 files changed, 23 insertions(+), 23 deletions(-)
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 72dc9cf4ef96e..5846acff120fc 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -555,7 +555,7 @@ 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_cpp_trivially_relocatable, IsCppTriviallyRelocatable, KEYCXX)
TYPE_TRAIT_1(__builtin_is_replaceable, IsReplaceable, KEYCXX)
// Embarcadero Expression Traits
diff --git a/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp b/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
index 1ca641226a292..4b2eabda47a70 100644
--- a/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
+++ b/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
@@ -40,16 +40,16 @@ class H trivially_relocatable_if_eligible {
};
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]));
+static_assert(__builtin_is_cpp_trivially_relocatable(Incomplete)); // expected-error {{incomplete type 'Incomplete' used in type trait expression}}
+static_assert(__builtin_is_cpp_trivially_relocatable(Trivial));
+static_assert(__builtin_is_cpp_trivially_relocatable(G));
+static_assert(__builtin_is_cpp_trivially_relocatable(H));
+static_assert(__builtin_is_cpp_trivially_relocatable(int));
+static_assert(__builtin_is_cpp_trivially_relocatable(void*));
+static_assert(!__builtin_is_cpp_trivially_relocatable(int&));
+static_assert(!__builtin_is_cpp_trivially_relocatable(Trivial&));
+static_assert(__builtin_is_cpp_trivially_relocatable(const Trivial));
+static_assert(__builtin_is_cpp_trivially_relocatable(Trivial[1]));
struct UserDtr {
~UserDtr();
@@ -85,14 +85,14 @@ struct UserDeletedMove{
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));
+static_assert(!__builtin_is_cpp_trivially_relocatable(UserDtr));
+static_assert(__builtin_is_cpp_trivially_relocatable(DefaultedDtr));
+static_assert(!__builtin_is_cpp_trivially_relocatable(UserMoveWithDefaultCopy));
+static_assert(!__builtin_is_cpp_trivially_relocatable(UserMove));
+static_assert(!__builtin_is_cpp_trivially_relocatable(UserCopy));
+static_assert(__builtin_is_cpp_trivially_relocatable(UserMoveDefault));
+static_assert(__builtin_is_cpp_trivially_relocatable(UserCopyDefault));
+static_assert(__builtin_is_cpp_trivially_relocatable(UserDeletedMove));
template <typename T>
class TestDependentErrors trivially_relocatable_if_eligible : T {};
@@ -109,15 +109,15 @@ 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));
+static_assert(!__builtin_is_cpp_trivially_relocatable(DeletedMove));
+static_assert(!__builtin_is_cpp_trivially_relocatable(DeletedCopy));
+static_assert(!__builtin_is_cpp_trivially_relocatable(DeletedMoveAssign));
union U {
G g;
};
static_assert(!__is_trivially_copyable(U));
-static_assert(__is_cpp_trivially_relocatable(U));
+static_assert(__builtin_is_cpp_trivially_relocatable(U));
namespace replaceable {
>From 59b99360523cdd47d6ca44f77341d21e2fe34541 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Thu, 20 Feb 2025 17:09:02 +0100
Subject: [PATCH 4/4] add docs and fix variable names in SemaChecking
---
clang/docs/LanguageExtensions.rst | 21 +++++++++++++++++++++
clang/lib/Sema/SemaChecking.cpp | 12 ++++++------
2 files changed, 27 insertions(+), 6 deletions(-)
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 2a956ad5b2909..7e4e71299eb80 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1825,6 +1825,12 @@ The following type trait primitives are supported by Clang. Those traits marked
functionally equivalent to copying the underlying bytes and then dropping the
source object on the floor. This is true of trivial types and types which
were made trivially relocatable via the ``clang::trivial_abi`` attribute.
+* ``__builtin_is_cpp_trivially_relocatable`` (C++): Returns true if and object
+ is trivially relocatable, as defined by the C++26 standard.
+ Note that the caller code should ensure that if the object is polymorphic,
+ the dynamic type is of the most derived type.
+* ``__builtin_is_replaceable`` (C++): Returns true if and object
+ is replaceable, as defined by the C++26 standard.
* ``__is_trivially_equality_comparable`` (Clang): Returns true if comparing two
objects of the provided type is known to be equivalent to comparing their
object representations. Note that types containing padding bytes are never
@@ -3624,6 +3630,21 @@ Query for this feature with ``__has_builtin(__builtin_operator_new)`` or
replaceable global (de)allocation functions, but do support calling at least
``::operator new(size_t)`` and ``::operator delete(void*)``.
+
+``__builtin_trivially_relocate``
+-----------------------------------
+
+**Syntax**:
+
+.. code-block:: c
+
+ T* __builtin_trivially_relocate(T* dest, T* src, size_t count)
+
+Trivially relocates ``count`` objects of relocatable, complete type ``T``
+from ``src`` to ``dest`` and returns ``dest``.
+This builtin is used to implement ``std::trivially_relocate``.
+
+
``__builtin_preserve_access_index``
-----------------------------------
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 0d28a5eeaa969..14e33489858e1 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1882,15 +1882,15 @@ 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) {
+ QualType Dest = TheCall->getArg(0)->getType();
+ if (!Dest->isPointerType() || Dest.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();
+ QualType T = Dest->getPointeeType();
if (S.RequireCompleteType(TheCall->getBeginLoc(), T,
diag::err_incomplete_type))
return ExprError();
@@ -1903,10 +1903,10 @@ static ExprResult BuiltinTriviallyRelocate(Sema &S, CallExpr *TheCall) {
return ExprError();
}
- TheCall->setType(ArgTy);
+ TheCall->setType(Dest);
- QualType Dest = TheCall->getArg(1)->getType();
- if (Dest.getCanonicalType() != ArgTy.getCanonicalType()) {
+ QualType Src = TheCall->getArg(1)->getType();
+ if (Src.getCanonicalType() != Dest.getCanonicalType()) {
S.Diag(TheCall->getArg(0)->getExprLoc(),
diag::err_builtin_trivially_relocate_invalid_arg_type)
<< /*the same*/ 3;
More information about the cfe-commits
mailing list