Vendor import of clang release_50 branch r311606:

https://llvm.org/svn/llvm-project/cfe/branches/release_50@311606
This commit is contained in:
Dimitry Andric 2017-08-24 16:35:14 +00:00
parent 6aa46a19c5
commit a75fa8aaf2
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/vendor/clang/dist/; revision=322839
svn path=/vendor/clang/clang-release_50-r311606/; revision=322840; tag=vendor/clang/clang-release_50-r311606
29 changed files with 682 additions and 227 deletions

View File

@ -49,6 +49,15 @@ Major New Features
- ...
C++ coroutines
^^^^^^^^^^^^^^
`C++ coroutines TS
<http://open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4680.pdf>`_
implementation has landed. Use ``-fcoroutines-ts -stdlib=libc++`` to enable
coroutine support. Here is `an example
<https://wandbox.org/permlink/Dth1IO5q8Oe31ew2>`_ to get you started.
Improvements to Clang's diagnostics
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -58,6 +67,25 @@ Improvements to Clang's diagnostics
- -Wunused-lambda-capture warns when a variable explicitly captured
by a lambda is not used in the body of the lambda.
- -Wstrict-prototypes is a new warning that warns about non-prototype
function and block declarations and types in C and Objective-C.
- -Wunguarded-availability is a new warning that warns about uses of new
APIs that were introduced in a system whose version is newer than the
deployment target version. A new Objective-C expression ``@available`` has
been introduced to perform system version checking at runtime. This warning
is off by default to prevent unexpected warnings in existing projects.
However, its less strict sibling -Wunguarded-availability-new is on by
default. It warns about unguarded uses of APIs only when they were introduced
in or after macOS 10.13, iOS 11, tvOS 11 or watchOS 4.
- The -Wdocumentation warning now allows the use of ``\param`` and
``\returns`` documentation directives in the documentation comments for
declarations with a function or a block pointer type.
- The compiler no longer warns about unreachable ``__builtin_unreachable``
statements.
New Compiler Flags
------------------
@ -76,8 +104,12 @@ future versions of Clang.
New Pragmas in Clang
-----------------------
Clang now supports the ...
- Clang now supports the ``clang attribute`` pragma that allows users to apply
an attribute to multiple declarations.
- ``pragma pack`` directives that are included in a precompiled header are now
applied correctly to the declarations in the compilation unit that includes
that precompiled header.
Attribute Changes in Clang
--------------------------
@ -85,6 +117,8 @@ Attribute Changes in Clang
- The ``overloadable`` attribute now allows at most one function with a given
name to lack the ``overloadable`` attribute. This unmarked function will not
have its name mangled.
- The ```ms_abi`` attribute and the ``__builtin_ms_va_list`` types and builtins
are now supported on AArch64.
Windows Support
---------------
@ -95,7 +129,41 @@ Clang's support for building native Windows programs ...
C Language Changes in Clang
---------------------------
- ...
- Added near complete support for implicit scalar to vector conversion, a GNU
C/C++ language extension. With this extension, the following code is
considered valid:
.. code-block:: c
typedef unsigned v4i32 __attribute__((vector_size(16)));
v4i32 foo(v4i32 a) {
// Here 5 is implicitly casted to an unsigned value and replicated into a
// vector with as many elements as 'a'.
return a + 5;
}
The implicit conversion of a scalar value to a vector value--in the context of
a vector expression--occurs when:
- The type of the vector is that of a ``__attribute__((vector_size(size)))``
vector, not an OpenCL ``__attribute__((ext_vector_type(size)))`` vector type.
- The scalar value can be casted to that of the vector element's type without
the loss of precision based on the type of the scalar and the type of the
vector's elements.
- For compile time constant values, the above rule is weakened to consider the
value of the scalar constant rather than the constant's type.
- Floating point constants with precise integral representations are not
implicitly converted to integer values, this is for compatability with GCC.
Currently the basic integer and floating point types with the following
operators are supported: ``+``, ``/``, ``-``, ``*``, ``%``, ``>``, ``<``,
``>=``, ``<=``, ``==``, ``!=``, ``&``, ``|``, ``^`` and the corresponding
assignment operators where applicable.
...
@ -107,6 +175,10 @@ C11 Feature Support
C++ Language Changes in Clang
-----------------------------
- As mentioned in `C Language Changes in Clang`_, Clang's support for
implicit scalar to vector conversions also applies to C++. Additionally
the following operators are also supported: ``&&`` and ``||``.
...
C++1z Feature Support
@ -117,12 +189,56 @@ C++1z Feature Support
Objective-C Language Changes in Clang
-------------------------------------
...
- Clang now guarantees that a ``readwrite`` property is synthesized when an
ambiguous property (i.e. a property that's declared in multiple protocols)
is synthesized. The ``-Wprotocol-property-synthesis-ambiguity`` warning that
warns about incompatible property types is now promoted to an error when
there's an ambiguity between ``readwrite`` and ``readonly`` properties.
- Clang now prohibits synthesis of ambiguous properties with incompatible
explicit property attributes. The following property attributes are
checked for differences: ``copy``, ``retain``/``strong``, ``atomic``,
``getter`` and ``setter``.
OpenCL C Language Changes in Clang
----------------------------------
...
Various bug fixes and improvements:
- Extended OpenCL-related Clang tests.
- Improved diagnostics across several areas: scoped address space
qualified variables, function pointers, atomics, type rank for overloading,
block captures, ``reserve_id_t``.
- Several address space related fixes for constant address space function scope variables,
IR generation, mangling of ``generic`` and alloca (post-fix from general Clang
refactoring of address spaces).
- Several improvements in extensions: fixed OpenCL version for ``cl_khr_mipmap_image``,
added missing ``cl_khr_3d_image_writes``.
- Improvements in ``enqueue_kernel``, especially the implementation of ``ndrange_t`` and blocks.
- OpenCL type related fixes: global samplers, the ``pipe_t`` size, internal type redefinition,
and type compatibility checking in ternary and other operations.
- The OpenCL header has been extended with missing extension guards, and direct mapping of ``as_type``
to ``__builtin_astype``.
- Fixed ``kernel_arg_type_qual`` and OpenCL/SPIR version in metadata.
- Added proper use of the kernel calling convention to various targets.
The following new functionalities have been added:
- Added documentation on OpenCL to Clang user manual.
- Extended Clang builtins with required ``cl_khr_subgroups`` support.
- Add ``intel_reqd_sub_group_size`` attribute support.
- Added OpenCL types to ``CIndex``.
OpenMP Support in Clang
----------------------------------
@ -194,8 +310,30 @@ clang-format
libclang
--------
...
- Libclang now provides code-completion results for more C++ constructs
and keywords. The following keywords/identifiers are now included in the
code-completion results: ``static_assert``, ``alignas``, ``constexpr``,
``final``, ``noexcept``, ``override`` and ``thread_local``.
- Libclang now provides code-completion results for members from dependent
classes. For example:
.. code-block:: c++
template<typename T>
void appendValue(std::vector<T> &dest, const T &value) {
dest. // Relevant completion results are now shown after '.'
}
Note that code-completion results are still not provided when the member
expression includes a dependent base expression. For example:
.. code-block:: c++
template<typename T>
void appendValue(std::vector<std::vector<T>> &dest, const T &value) {
dest.at(0). // Libclang fails to provide completion results after '.'
}
Static Analyzer
---------------

View File

@ -375,6 +375,7 @@ class CXXRecordDecl : public RecordDecl {
/// \brief These flags are \c true if a defaulted corresponding special
/// member can't be fully analyzed without performing overload resolution.
/// @{
unsigned NeedOverloadResolutionForCopyConstructor : 1;
unsigned NeedOverloadResolutionForMoveConstructor : 1;
unsigned NeedOverloadResolutionForMoveAssignment : 1;
unsigned NeedOverloadResolutionForDestructor : 1;
@ -383,6 +384,7 @@ class CXXRecordDecl : public RecordDecl {
/// \brief These flags are \c true if an implicit defaulted corresponding
/// special member would be defined as deleted.
/// @{
unsigned DefaultedCopyConstructorIsDeleted : 1;
unsigned DefaultedMoveConstructorIsDeleted : 1;
unsigned DefaultedMoveAssignmentIsDeleted : 1;
unsigned DefaultedDestructorIsDeleted : 1;
@ -415,6 +417,12 @@ class CXXRecordDecl : public RecordDecl {
/// constructor.
unsigned HasDefaultedDefaultConstructor : 1;
/// \brief True if this class can be passed in a non-address-preserving
/// fashion (such as in registers) according to the C++ language rules.
/// This does not imply anything about how the ABI in use will actually
/// pass an object of this class.
unsigned CanPassInRegisters : 1;
/// \brief True if a defaulted default constructor for this class would
/// be constexpr.
unsigned DefaultedDefaultConstructorIsConstexpr : 1;
@ -811,18 +819,50 @@ class CXXRecordDecl : public RecordDecl {
return data().FirstFriend.isValid();
}
/// \brief \c true if a defaulted copy constructor for this class would be
/// deleted.
bool defaultedCopyConstructorIsDeleted() const {
assert((!needsOverloadResolutionForCopyConstructor() ||
(data().DeclaredSpecialMembers & SMF_CopyConstructor)) &&
"this property has not yet been computed by Sema");
return data().DefaultedCopyConstructorIsDeleted;
}
/// \brief \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");
return data().DefaultedMoveConstructorIsDeleted;
}
/// \brief \c true if a defaulted destructor for this class would be deleted.
bool defaultedDestructorIsDeleted() const {
return !data().DefaultedDestructorIsDeleted;
}
/// \brief \c true if we know for sure that this class has a single,
/// accessible, unambiguous copy constructor that is not deleted.
bool hasSimpleCopyConstructor() const {
return !hasUserDeclaredCopyConstructor() &&
!data().DefaultedCopyConstructorIsDeleted;
}
/// \brief \c true if we know for sure that this class has a single,
/// accessible, unambiguous move constructor that is not deleted.
bool hasSimpleMoveConstructor() const {
return !hasUserDeclaredMoveConstructor() && hasMoveConstructor() &&
!data().DefaultedMoveConstructorIsDeleted;
}
/// \brief \c true if we know for sure that this class has a single,
/// accessible, unambiguous move assignment operator that is not deleted.
bool hasSimpleMoveAssignment() const {
return !hasUserDeclaredMoveAssignment() && hasMoveAssignment() &&
!data().DefaultedMoveAssignmentIsDeleted;
}
/// \brief \c true if we know for sure that this class has an accessible
/// destructor that is not deleted.
bool hasSimpleDestructor() const {
@ -878,7 +918,16 @@ class CXXRecordDecl : public RecordDecl {
/// \brief Determine whether we need to eagerly declare a defaulted copy
/// constructor for this class.
bool needsOverloadResolutionForCopyConstructor() const {
return data().HasMutableFields;
// C++17 [class.copy.ctor]p6:
// If the class definition declares a move constructor or move assignment
// operator, the implicitly declared copy constructor is defined as
// deleted.
// In MSVC mode, sometimes a declared move assignment does not delete an
// implicit copy constructor, so defer this choice to Sema.
if (data().UserDeclaredSpecialMembers &
(SMF_MoveConstructor | SMF_MoveAssignment))
return true;
return data().NeedOverloadResolutionForCopyConstructor;
}
/// \brief Determine whether an implicit copy constructor for this type
@ -919,7 +968,16 @@ class CXXRecordDecl : public RecordDecl {
needsImplicitMoveConstructor();
}
/// \brief Set that we attempted to declare an implicitly move
/// \brief Set that we attempted to declare an implicit copy
/// constructor, but overload resolution failed so we deleted it.
void setImplicitCopyConstructorIsDeleted() {
assert((data().DefaultedCopyConstructorIsDeleted ||
needsOverloadResolutionForCopyConstructor()) &&
"Copy constructor should not be deleted");
data().DefaultedCopyConstructorIsDeleted = true;
}
/// \brief Set that we attempted to declare an implicit move
/// constructor, but overload resolution failed so we deleted it.
void setImplicitMoveConstructorIsDeleted() {
assert((data().DefaultedMoveConstructorIsDeleted ||
@ -1316,6 +1374,18 @@ class CXXRecordDecl : public RecordDecl {
return data().HasIrrelevantDestructor;
}
/// \brief Determine whether this class has at least one trivial, non-deleted
/// copy or move constructor.
bool canPassInRegisters() const {
return data().CanPassInRegisters;
}
/// \brief Set that we can pass this RecordDecl in registers.
// FIXME: This should be set as part of completeDefinition.
void setCanPassInRegisters(bool CanPass) {
data().CanPassInRegisters = CanPass;
}
/// \brief Determine whether this class has a non-literal or/ volatile type
/// non-static data member or base class.
bool hasNonLiteralTypeFieldsOrBases() const {

View File

@ -1048,10 +1048,6 @@ class Preprocessor {
/// which implicitly adds the builtin defines etc.
void EnterMainSourceFile();
/// \brief After parser warm-up, initialize the conditional stack from
/// the preamble.
void replayPreambleConditionalStack();
/// \brief Inform the preprocessor callbacks that processing is complete.
void EndSourceFile();
@ -2025,6 +2021,10 @@ class Preprocessor {
}
private:
/// \brief After processing predefined file, initialize the conditional stack from
/// the preamble.
void replayPreambleConditionalStack();
// Macro handling.
void HandleDefineDirective(Token &Tok, bool ImmediatelyAfterTopLevelIfndef);
void HandleUndefDirective();

View File

@ -956,12 +956,16 @@ bool ASTNodeImporter::ImportDefinition(RecordDecl *From, RecordDecl *To,
ToData.HasUninitializedFields = FromData.HasUninitializedFields;
ToData.HasInheritedConstructor = FromData.HasInheritedConstructor;
ToData.HasInheritedAssignment = FromData.HasInheritedAssignment;
ToData.NeedOverloadResolutionForCopyConstructor
= FromData.NeedOverloadResolutionForCopyConstructor;
ToData.NeedOverloadResolutionForMoveConstructor
= FromData.NeedOverloadResolutionForMoveConstructor;
ToData.NeedOverloadResolutionForMoveAssignment
= FromData.NeedOverloadResolutionForMoveAssignment;
ToData.NeedOverloadResolutionForDestructor
= FromData.NeedOverloadResolutionForDestructor;
ToData.DefaultedCopyConstructorIsDeleted
= FromData.DefaultedCopyConstructorIsDeleted;
ToData.DefaultedMoveConstructorIsDeleted
= FromData.DefaultedMoveConstructorIsDeleted;
ToData.DefaultedMoveAssignmentIsDeleted
@ -973,6 +977,7 @@ bool ASTNodeImporter::ImportDefinition(RecordDecl *From, RecordDecl *To,
= FromData.HasConstexprNonCopyMoveConstructor;
ToData.HasDefaultedDefaultConstructor
= FromData.HasDefaultedDefaultConstructor;
ToData.CanPassInRegisters = FromData.CanPassInRegisters;
ToData.DefaultedDefaultConstructorIsConstexpr
= FromData.DefaultedDefaultConstructorIsConstexpr;
ToData.HasConstexprDefaultConstructor

View File

@ -55,15 +55,18 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
HasOnlyCMembers(true), HasInClassInitializer(false),
HasUninitializedReferenceMember(false), HasUninitializedFields(false),
HasInheritedConstructor(false), HasInheritedAssignment(false),
NeedOverloadResolutionForCopyConstructor(false),
NeedOverloadResolutionForMoveConstructor(false),
NeedOverloadResolutionForMoveAssignment(false),
NeedOverloadResolutionForDestructor(false),
DefaultedCopyConstructorIsDeleted(false),
DefaultedMoveConstructorIsDeleted(false),
DefaultedMoveAssignmentIsDeleted(false),
DefaultedDestructorIsDeleted(false), HasTrivialSpecialMembers(SMF_All),
DeclaredNonTrivialSpecialMembers(0), HasIrrelevantDestructor(true),
HasConstexprNonCopyMoveConstructor(false),
HasDefaultedDefaultConstructor(false),
CanPassInRegisters(true),
DefaultedDefaultConstructorIsConstexpr(true),
HasConstexprDefaultConstructor(false),
HasNonLiteralTypeFieldsOrBases(false), ComputedVisibleConversions(false),
@ -352,8 +355,10 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
setHasVolatileMember(true);
// Keep track of the presence of mutable fields.
if (BaseClassDecl->hasMutableFields())
if (BaseClassDecl->hasMutableFields()) {
data().HasMutableFields = true;
data().NeedOverloadResolutionForCopyConstructor = true;
}
if (BaseClassDecl->hasUninitializedReferenceMember())
data().HasUninitializedReferenceMember = true;
@ -406,6 +411,8 @@ void CXXRecordDecl::addedClassSubobject(CXXRecordDecl *Subobj) {
// -- a direct or virtual base class B that cannot be copied/moved [...]
// -- a non-static data member of class type M (or array thereof)
// that cannot be copied or moved [...]
if (!Subobj->hasSimpleCopyConstructor())
data().NeedOverloadResolutionForCopyConstructor = true;
if (!Subobj->hasSimpleMoveConstructor())
data().NeedOverloadResolutionForMoveConstructor = true;
@ -426,6 +433,7 @@ void CXXRecordDecl::addedClassSubobject(CXXRecordDecl *Subobj) {
// -- any non-static data member has a type with a destructor
// that is deleted or inaccessible from the defaulted [ctor or dtor].
if (!Subobj->hasSimpleDestructor()) {
data().NeedOverloadResolutionForCopyConstructor = true;
data().NeedOverloadResolutionForMoveConstructor = true;
data().NeedOverloadResolutionForDestructor = true;
}
@ -711,8 +719,10 @@ void CXXRecordDecl::addedMember(Decl *D) {
data().IsStandardLayout = false;
// Keep track of the presence of mutable fields.
if (Field->isMutable())
if (Field->isMutable()) {
data().HasMutableFields = true;
data().NeedOverloadResolutionForCopyConstructor = true;
}
// C++11 [class.union]p8, DR1460:
// If X is a union, a non-static data member of X that is not an anonymous
@ -756,6 +766,12 @@ void CXXRecordDecl::addedMember(Decl *D) {
// A standard-layout class is a class that:
// -- has no non-static data members of type [...] reference,
data().IsStandardLayout = false;
// C++1z [class.copy.ctor]p10:
// A defaulted copy constructor for a class X is defined as deleted if X has:
// -- a non-static data member of rvalue reference type
if (T->isRValueReferenceType())
data().DefaultedCopyConstructorIsDeleted = true;
}
if (!Field->hasInClassInitializer() && !Field->isMutable()) {
@ -809,6 +825,10 @@ void CXXRecordDecl::addedMember(Decl *D) {
// We may need to perform overload resolution to determine whether a
// field can be moved if it's const or volatile qualified.
if (T.getCVRQualifiers() & (Qualifiers::Const | Qualifiers::Volatile)) {
// We need to care about 'const' for the copy constructor because an
// implicit copy constructor might be declared with a non-const
// parameter.
data().NeedOverloadResolutionForCopyConstructor = true;
data().NeedOverloadResolutionForMoveConstructor = true;
data().NeedOverloadResolutionForMoveAssignment = true;
}
@ -819,6 +839,8 @@ void CXXRecordDecl::addedMember(Decl *D) {
// -- X is a union-like class that has a variant member with a
// non-trivial [corresponding special member]
if (isUnion()) {
if (FieldRec->hasNonTrivialCopyConstructor())
data().DefaultedCopyConstructorIsDeleted = true;
if (FieldRec->hasNonTrivialMoveConstructor())
data().DefaultedMoveConstructorIsDeleted = true;
if (FieldRec->hasNonTrivialMoveAssignment())
@ -830,6 +852,8 @@ void CXXRecordDecl::addedMember(Decl *D) {
// For an anonymous union member, our overload resolution will perform
// overload resolution for its members.
if (Field->isAnonymousStructOrUnion()) {
data().NeedOverloadResolutionForCopyConstructor |=
FieldRec->data().NeedOverloadResolutionForCopyConstructor;
data().NeedOverloadResolutionForMoveConstructor |=
FieldRec->data().NeedOverloadResolutionForMoveConstructor;
data().NeedOverloadResolutionForMoveAssignment |=
@ -915,8 +939,10 @@ void CXXRecordDecl::addedMember(Decl *D) {
}
// Keep track of the presence of mutable fields.
if (FieldRec->hasMutableFields())
if (FieldRec->hasMutableFields()) {
data().HasMutableFields = true;
data().NeedOverloadResolutionForCopyConstructor = true;
}
// C++11 [class.copy]p13:
// If the implicitly-defined constructor would satisfy the
@ -1450,7 +1476,7 @@ void CXXRecordDecl::completeDefinition() {
void CXXRecordDecl::completeDefinition(CXXFinalOverriderMap *FinalOverriders) {
RecordDecl::completeDefinition();
// If the class may be abstract (but hasn't been marked as such), check for
// any pure final overriders.
if (mayBeAbstract()) {

View File

@ -30,38 +30,9 @@ void CGCXXABI::ErrorUnsupportedABI(CodeGenFunction &CGF, StringRef S) {
}
bool CGCXXABI::canCopyArgument(const CXXRecordDecl *RD) const {
// If RD has a non-trivial move or copy constructor, we cannot copy the
// argument.
if (RD->hasNonTrivialCopyConstructor() || RD->hasNonTrivialMoveConstructor())
return false;
// If RD has a non-trivial destructor, we cannot copy the argument.
if (RD->hasNonTrivialDestructor())
return false;
// We can only copy the argument if there exists at least one trivial,
// non-deleted copy or move constructor.
// FIXME: This assumes that all lazily declared copy and move constructors are
// not deleted. This assumption might not be true in some corner cases.
bool CopyDeleted = false;
bool MoveDeleted = false;
for (const CXXConstructorDecl *CD : RD->ctors()) {
if (CD->isCopyConstructor() || CD->isMoveConstructor()) {
assert(CD->isTrivial());
// We had at least one undeleted trivial copy or move ctor. Return
// directly.
if (!CD->isDeleted())
return true;
if (CD->isCopyConstructor())
CopyDeleted = true;
else
MoveDeleted = true;
}
}
// If all trivial copy and move constructors are deleted, we cannot copy the
// argument.
return !(CopyDeleted && MoveDeleted);
return RD->canPassInRegisters();
}
llvm::Constant *CGCXXABI::GetBogusMemberPointer(QualType T) {

View File

@ -63,11 +63,8 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
bool classifyReturnType(CGFunctionInfo &FI) const override;
RecordArgABI getRecordArgABI(const CXXRecordDecl *RD) const override {
// Structures with either a non-trivial destructor or a non-trivial
// copy constructor are always indirect.
// FIXME: Use canCopyArgument() when it is fixed to handle lazily declared
// special members.
if (RD->hasNonTrivialDestructor() || RD->hasNonTrivialCopyConstructor())
// If C++ prohibits us from making a copy, pass by address.
if (!canCopyArgument(RD))
return RAA_Indirect;
return RAA_Default;
}
@ -998,10 +995,8 @@ bool ItaniumCXXABI::classifyReturnType(CGFunctionInfo &FI) const {
if (!RD)
return false;
// Return indirectly if we have a non-trivial copy ctor or non-trivial dtor.
// FIXME: Use canCopyArgument() when it is fixed to handle lazily declared
// special members.
if (RD->hasNonTrivialDestructor() || RD->hasNonTrivialCopyConstructor()) {
// If C++ prohibits us from making a copy, return by address.
if (!canCopyArgument(RD)) {
auto Align = CGM.getContext().getTypeAlignInChars(FI.getReturnType());
FI.getReturnInfo() = ABIArgInfo::getIndirect(Align, /*ByVal=*/false);
return true;

View File

@ -819,46 +819,44 @@ MicrosoftCXXABI::getRecordArgABI(const CXXRecordDecl *RD) const {
return RAA_Default;
case llvm::Triple::x86_64:
// Win64 passes objects with non-trivial copy ctors indirectly.
if (RD->hasNonTrivialCopyConstructor())
return RAA_Indirect;
// If an object has a destructor, we'd really like to pass it indirectly
// If a class has a destructor, we'd really like to pass it indirectly
// because it allows us to elide copies. Unfortunately, MSVC makes that
// impossible for small types, which it will pass in a single register or
// stack slot. Most objects with dtors are large-ish, so handle that early.
// We can't call out all large objects as being indirect because there are
// multiple x64 calling conventions and the C++ ABI code shouldn't dictate
// how we pass large POD types.
//
// Note: This permits small classes with nontrivial destructors to be
// passed in registers, which is non-conforming.
if (RD->hasNonTrivialDestructor() &&
getContext().getTypeSize(RD->getTypeForDecl()) > 64)
return RAA_Indirect;
// If this is true, the implicit copy constructor that Sema would have
// created would not be deleted. FIXME: We should provide a more direct way
// for CodeGen to ask whether the constructor was deleted.
if (!RD->hasUserDeclaredCopyConstructor() &&
!RD->hasUserDeclaredMoveConstructor() &&
!RD->needsOverloadResolutionForMoveConstructor() &&
!RD->hasUserDeclaredMoveAssignment() &&
!RD->needsOverloadResolutionForMoveAssignment())
return RAA_Default;
// Otherwise, Sema should have created an implicit copy constructor if
// needed.
assert(!RD->needsImplicitCopyConstructor());
// We have to make sure the trivial copy constructor isn't deleted.
for (const CXXConstructorDecl *CD : RD->ctors()) {
if (CD->isCopyConstructor()) {
assert(CD->isTrivial());
// We had at least one undeleted trivial copy ctor. Return directly.
if (!CD->isDeleted())
return RAA_Default;
// If a class has at least one non-deleted, trivial copy constructor, it
// is passed according to the C ABI. Otherwise, it is passed indirectly.
//
// Note: This permits classes with non-trivial copy or move ctors to be
// passed in registers, so long as they *also* have a trivial copy ctor,
// which is non-conforming.
if (RD->needsImplicitCopyConstructor()) {
// If the copy ctor has not yet been declared, we can read its triviality
// off the AST.
if (!RD->defaultedCopyConstructorIsDeleted() &&
RD->hasTrivialCopyConstructor())
return RAA_Default;
} else {
// Otherwise, we need to find the copy constructor(s) and ask.
for (const CXXConstructorDecl *CD : RD->ctors()) {
if (CD->isCopyConstructor()) {
// We had at least one nondeleted trivial copy ctor. Return directly.
if (!CD->isDeleted() && CD->isTrivial())
return RAA_Default;
}
}
}
// The trivial copy constructor was deleted. Return indirectly.
// We have no trivial, non-deleted copy constructor.
return RAA_Indirect;
}

View File

@ -1837,7 +1837,12 @@ Darwin::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch,
}
bool MachO::IsUnwindTablesDefault(const ArgList &Args) const {
return !UseSjLjExceptions(Args);
// Unwind tables are not emitted if -fno-exceptions is supplied (except when
// targeting x86_64).
return getArch() == llvm::Triple::x86_64 ||
(!UseSjLjExceptions(Args) &&
Args.hasFlag(options::OPT_fexceptions, options::OPT_fno_exceptions,
true));
}
bool MachO::UseDwarfDebugFlags() const {

View File

@ -76,7 +76,7 @@ static bool getSystemRegistryString(const char *keyPath, const char *valueName,
// Check various environment variables to try and find a toolchain.
static bool findVCToolChainViaEnvironment(std::string &Path,
bool &IsVS2017OrNewer) {
MSVCToolChain::ToolsetLayout &VSLayout) {
// These variables are typically set by vcvarsall.bat
// when launching a developer command prompt.
if (llvm::Optional<std::string> VCToolsInstallDir =
@ -84,7 +84,7 @@ static bool findVCToolChainViaEnvironment(std::string &Path,
// This is only set by newer Visual Studios, and it leads straight to
// the toolchain directory.
Path = std::move(*VCToolsInstallDir);
IsVS2017OrNewer = true;
VSLayout = MSVCToolChain::ToolsetLayout::VS2017OrNewer;
return true;
}
if (llvm::Optional<std::string> VCInstallDir =
@ -94,7 +94,7 @@ static bool findVCToolChainViaEnvironment(std::string &Path,
// so this check has to appear second.
// In older Visual Studios, the VC directory is the toolchain.
Path = std::move(*VCInstallDir);
IsVS2017OrNewer = false;
VSLayout = MSVCToolChain::ToolsetLayout::OlderVS;
return true;
}
@ -134,9 +134,16 @@ static bool findVCToolChainViaEnvironment(std::string &Path,
}
if (IsBin) {
llvm::StringRef ParentPath = llvm::sys::path::parent_path(TestPath);
if (llvm::sys::path::filename(ParentPath) == "VC") {
llvm::StringRef ParentFilename = llvm::sys::path::filename(ParentPath);
if (ParentFilename == "VC") {
Path = ParentPath;
IsVS2017OrNewer = false;
VSLayout = MSVCToolChain::ToolsetLayout::OlderVS;
return true;
}
if (ParentFilename == "x86ret" || ParentFilename == "x86chk"
|| ParentFilename == "amd64ret" || ParentFilename == "amd64chk") {
Path = ParentPath;
VSLayout = MSVCToolChain::ToolsetLayout::DevDivInternal;
return true;
}
@ -165,7 +172,7 @@ static bool findVCToolChainViaEnvironment(std::string &Path,
ToolChainPath = llvm::sys::path::parent_path(ToolChainPath);
Path = ToolChainPath;
IsVS2017OrNewer = true;
VSLayout = MSVCToolChain::ToolsetLayout::VS2017OrNewer;
return true;
}
@ -181,7 +188,7 @@ static bool findVCToolChainViaEnvironment(std::string &Path,
// This is the preferred way to discover new Visual Studios, as they're no
// longer listed in the registry.
static bool findVCToolChainViaSetupConfig(std::string &Path,
bool &IsVS2017OrNewer) {
MSVCToolChain::ToolsetLayout &VSLayout) {
#if !defined(USE_MSVC_SETUP_API)
return false;
#else
@ -263,7 +270,7 @@ static bool findVCToolChainViaSetupConfig(std::string &Path,
return false;
Path = ToolchainPath.str();
IsVS2017OrNewer = true;
VSLayout = MSVCToolChain::ToolsetLayout::VS2017OrNewer;
return true;
#endif
}
@ -272,7 +279,7 @@ static bool findVCToolChainViaSetupConfig(std::string &Path,
// a toolchain path. VS2017 and newer don't get added to the registry.
// So if we find something here, we know that it's an older version.
static bool findVCToolChainViaRegistry(std::string &Path,
bool &IsVS2017OrNewer) {
MSVCToolChain::ToolsetLayout &VSLayout) {
std::string VSInstallPath;
if (getSystemRegistryString(R"(SOFTWARE\Microsoft\VisualStudio\$VERSION)",
"InstallDir", VSInstallPath, nullptr) ||
@ -284,7 +291,7 @@ static bool findVCToolChainViaRegistry(std::string &Path,
llvm::sys::path::append(VCPath, "VC");
Path = VCPath.str();
IsVS2017OrNewer = false;
VSLayout = MSVCToolChain::ToolsetLayout::OlderVS;
return true;
}
}
@ -475,6 +482,7 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
// native target bin directory.
// e.g. when compiling for x86 on an x64 host, PATH should start with:
// /bin/HostX64/x86;/bin/HostX64/x64
// This doesn't attempt to handle ToolsetLayout::DevDivInternal.
if (TC.getIsVS2017OrNewer() &&
llvm::Triple(llvm::sys::getProcessTriple()).getArch() != TC.getArch()) {
auto HostArch = llvm::Triple(llvm::sys::getProcessTriple()).getArch();
@ -677,9 +685,9 @@ MSVCToolChain::MSVCToolChain(const Driver &D, const llvm::Triple &Triple,
// what they want to use.
// Failing that, just try to find the newest Visual Studio version we can
// and use its default VC toolchain.
findVCToolChainViaEnvironment(VCToolChainPath, IsVS2017OrNewer) ||
findVCToolChainViaSetupConfig(VCToolChainPath, IsVS2017OrNewer) ||
findVCToolChainViaRegistry(VCToolChainPath, IsVS2017OrNewer);
findVCToolChainViaEnvironment(VCToolChainPath, VSLayout) ||
findVCToolChainViaSetupConfig(VCToolChainPath, VSLayout) ||
findVCToolChainViaRegistry(VCToolChainPath, VSLayout);
}
Tool *MSVCToolChain::buildLinker() const {
@ -766,6 +774,21 @@ static const char *llvmArchToLegacyVCArch(llvm::Triple::ArchType Arch) {
}
}
// Similar to the above function, but for DevDiv internal builds.
static const char *llvmArchToDevDivInternalArch(llvm::Triple::ArchType Arch) {
using ArchType = llvm::Triple::ArchType;
switch (Arch) {
case ArchType::x86:
return "i386";
case ArchType::x86_64:
return "amd64";
case ArchType::arm:
return "arm";
default:
return "";
}
}
// Get the path to a specific subdirectory in the current toolchain for
// a given target architecture.
// VS2017 changed the VC toolchain layout, so this should be used instead
@ -773,26 +796,40 @@ static const char *llvmArchToLegacyVCArch(llvm::Triple::ArchType Arch) {
std::string
MSVCToolChain::getSubDirectoryPath(SubDirectoryType Type,
llvm::Triple::ArchType TargetArch) const {
const char *SubdirName;
const char *IncludeName;
switch (VSLayout) {
case ToolsetLayout::OlderVS:
SubdirName = llvmArchToLegacyVCArch(TargetArch);
IncludeName = "include";
break;
case ToolsetLayout::VS2017OrNewer:
SubdirName = llvmArchToWindowsSDKArch(TargetArch);
IncludeName = "include";
break;
case ToolsetLayout::DevDivInternal:
SubdirName = llvmArchToDevDivInternalArch(TargetArch);
IncludeName = "inc";
break;
}
llvm::SmallString<256> Path(VCToolChainPath);
switch (Type) {
case SubDirectoryType::Bin:
if (IsVS2017OrNewer) {
bool HostIsX64 =
if (VSLayout == ToolsetLayout::VS2017OrNewer) {
const bool HostIsX64 =
llvm::Triple(llvm::sys::getProcessTriple()).isArch64Bit();
llvm::sys::path::append(Path, "bin", (HostIsX64 ? "HostX64" : "HostX86"),
llvmArchToWindowsSDKArch(TargetArch));
} else {
llvm::sys::path::append(Path, "bin", llvmArchToLegacyVCArch(TargetArch));
const char *const HostName = HostIsX64 ? "HostX64" : "HostX86";
llvm::sys::path::append(Path, "bin", HostName, SubdirName);
} else { // OlderVS or DevDivInternal
llvm::sys::path::append(Path, "bin", SubdirName);
}
break;
case SubDirectoryType::Include:
llvm::sys::path::append(Path, "include");
llvm::sys::path::append(Path, IncludeName);
break;
case SubDirectoryType::Lib:
llvm::sys::path::append(
Path, "lib", IsVS2017OrNewer ? llvmArchToWindowsSDKArch(TargetArch)
: llvmArchToLegacyVCArch(TargetArch));
llvm::sys::path::append(Path, "lib", SubdirName);
break;
}
return Path.str();

View File

@ -92,7 +92,12 @@ class LLVM_LIBRARY_VISIBILITY MSVCToolChain : public ToolChain {
return getSubDirectoryPath(Type, getArch());
}
bool getIsVS2017OrNewer() const { return IsVS2017OrNewer; }
enum class ToolsetLayout {
OlderVS,
VS2017OrNewer,
DevDivInternal,
};
bool getIsVS2017OrNewer() const { return VSLayout == ToolsetLayout::VS2017OrNewer; }
void
AddClangSystemIncludeArgs(const llvm::opt::ArgList &DriverArgs,
@ -130,7 +135,7 @@ class LLVM_LIBRARY_VISIBILITY MSVCToolChain : public ToolChain {
Tool *buildAssembler() const override;
private:
std::string VCToolChainPath;
bool IsVS2017OrNewer = false;
ToolsetLayout VSLayout = ToolsetLayout::OlderVS;
CudaInstallationDetector CudaInstallation;
};

View File

@ -472,9 +472,14 @@ void WhitespaceManager::alignTrailingComments() {
continue;
unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
unsigned ChangeMaxColumn = Style.ColumnLimit >= Changes[i].TokenLength
? Style.ColumnLimit - Changes[i].TokenLength
: ChangeMinColumn;
unsigned ChangeMaxColumn;
if (Style.ColumnLimit == 0)
ChangeMaxColumn = UINT_MAX;
else if (Style.ColumnLimit >= Changes[i].TokenLength)
ChangeMaxColumn = Style.ColumnLimit - Changes[i].TokenLength;
else
ChangeMaxColumn = ChangeMinColumn;
// If we don't create a replacement for this change, we have to consider
// it to be immovable.

View File

@ -76,13 +76,7 @@ typedef intptr_t _sleb128_t;
typedef uintptr_t _uleb128_t;
struct _Unwind_Context;
#if defined(__arm__) && !(defined(__USING_SJLJ_EXCEPTIONS__) || defined(__ARM_DWARF_EH___))
struct _Unwind_Control_Block;
typedef struct _Unwind_Control_Block _Unwind_Exception; /* Alias */
#else
struct _Unwind_Exception;
typedef struct _Unwind_Exception _Unwind_Exception;
#endif
typedef enum {
_URC_NO_REASON = 0,
#if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) && \
@ -115,42 +109,8 @@ typedef enum {
} _Unwind_Action;
typedef void (*_Unwind_Exception_Cleanup_Fn)(_Unwind_Reason_Code,
_Unwind_Exception *);
struct _Unwind_Exception *);
#if defined(__arm__) && !(defined(__USING_SJLJ_EXCEPTIONS__) || defined(__ARM_DWARF_EH___))
typedef struct _Unwind_Control_Block _Unwind_Control_Block;
typedef uint32_t _Unwind_EHT_Header;
struct _Unwind_Control_Block {
uint64_t exception_class;
void (*exception_cleanup)(_Unwind_Reason_Code, _Unwind_Control_Block *);
/* unwinder cache (private fields for the unwinder's use) */
struct {
uint32_t reserved1; /* forced unwind stop function, 0 if not forced */
uint32_t reserved2; /* personality routine */
uint32_t reserved3; /* callsite */
uint32_t reserved4; /* forced unwind stop argument */
uint32_t reserved5;
} unwinder_cache;
/* propagation barrier cache (valid after phase 1) */
struct {
uint32_t sp;
uint32_t bitpattern[5];
} barrier_cache;
/* cleanup cache (preserved over cleanup) */
struct {
uint32_t bitpattern[4];
} cleanup_cache;
/* personality cache (for personality's benefit) */
struct {
uint32_t fnstart; /* function start address */
_Unwind_EHT_Header *ehtp; /* pointer to EHT entry header word */
uint32_t additional; /* additional data */
uint32_t reserved1;
} pr_cache;
long long int : 0; /* force alignment of next item to 8-byte boundary */
};
#else
struct _Unwind_Exception {
_Unwind_Exception_Class exception_class;
_Unwind_Exception_Cleanup_Fn exception_cleanup;
@ -160,24 +120,23 @@ struct _Unwind_Exception {
* aligned". GCC has interpreted this to mean "use the maximum useful
* alignment for the target"; so do we. */
} __attribute__((__aligned__));
#endif
typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn)(int, _Unwind_Action,
_Unwind_Exception_Class,
_Unwind_Exception *,
struct _Unwind_Exception *,
struct _Unwind_Context *,
void *);
typedef _Unwind_Reason_Code (*_Unwind_Personality_Fn)(int, _Unwind_Action,
_Unwind_Exception_Class,
_Unwind_Exception *,
struct _Unwind_Context *);
typedef _Unwind_Reason_Code (*_Unwind_Personality_Fn)(
int, _Unwind_Action, _Unwind_Exception_Class, struct _Unwind_Exception *,
struct _Unwind_Context *);
typedef _Unwind_Personality_Fn __personality_routine;
typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn)(struct _Unwind_Context *,
void *);
#if defined(__arm__) && !(defined(__USING_SJLJ_EXCEPTIONS__) || defined(__ARM_DWARF_EH___))
#if defined(__arm__) && !defined(__APPLE__)
typedef enum {
_UVRSC_CORE = 0, /* integer register */
_UVRSC_VFP = 1, /* vfp */
@ -199,12 +158,14 @@ typedef enum {
_UVRSR_FAILED = 2
} _Unwind_VRS_Result;
#if !defined(__USING_SJLJ_EXCEPTIONS__) && !defined(__ARM_DWARF_EH__)
typedef uint32_t _Unwind_State;
#define _US_VIRTUAL_UNWIND_FRAME ((_Unwind_State)0)
#define _US_UNWIND_FRAME_STARTING ((_Unwind_State)1)
#define _US_UNWIND_FRAME_RESUME ((_Unwind_State)2)
#define _US_ACTION_MASK ((_Unwind_State)3)
#define _US_FORCE_UNWIND ((_Unwind_State)8)
#endif
_Unwind_VRS_Result _Unwind_VRS_Get(struct _Unwind_Context *__context,
_Unwind_VRS_RegClass __regclass,
@ -263,12 +224,13 @@ _Unwind_Ptr _Unwind_GetRegionStart(struct _Unwind_Context *);
/* DWARF EH functions; currently not available on Darwin/ARM */
#if !defined(__APPLE__) || !defined(__arm__)
_Unwind_Reason_Code _Unwind_RaiseException(_Unwind_Exception *);
_Unwind_Reason_Code _Unwind_ForcedUnwind(_Unwind_Exception *, _Unwind_Stop_Fn,
void *);
void _Unwind_DeleteException(_Unwind_Exception *);
void _Unwind_Resume(_Unwind_Exception *);
_Unwind_Reason_Code _Unwind_Resume_or_Rethrow(_Unwind_Exception *);
_Unwind_Reason_Code _Unwind_RaiseException(struct _Unwind_Exception *);
_Unwind_Reason_Code _Unwind_ForcedUnwind(struct _Unwind_Exception *,
_Unwind_Stop_Fn, void *);
void _Unwind_DeleteException(struct _Unwind_Exception *);
void _Unwind_Resume(struct _Unwind_Exception *);
_Unwind_Reason_Code _Unwind_Resume_or_Rethrow(struct _Unwind_Exception *);
#endif
@ -279,11 +241,11 @@ typedef struct SjLj_Function_Context *_Unwind_FunctionContext_t;
void _Unwind_SjLj_Register(_Unwind_FunctionContext_t);
void _Unwind_SjLj_Unregister(_Unwind_FunctionContext_t);
_Unwind_Reason_Code _Unwind_SjLj_RaiseException(_Unwind_Exception *);
_Unwind_Reason_Code _Unwind_SjLj_ForcedUnwind(_Unwind_Exception *,
_Unwind_Reason_Code _Unwind_SjLj_RaiseException(struct _Unwind_Exception *);
_Unwind_Reason_Code _Unwind_SjLj_ForcedUnwind(struct _Unwind_Exception *,
_Unwind_Stop_Fn, void *);
void _Unwind_SjLj_Resume(_Unwind_Exception *);
_Unwind_Reason_Code _Unwind_SjLj_Resume_or_Rethrow(_Unwind_Exception *);
void _Unwind_SjLj_Resume(struct _Unwind_Exception *);
_Unwind_Reason_Code _Unwind_SjLj_Resume_or_Rethrow(struct _Unwind_Exception *);
void *_Unwind_FindEnclosingFunction(void *);

View File

@ -458,10 +458,16 @@ bool Preprocessor::HandleEndOfFile(Token &Result, bool isEndOfMacro) {
SourceMgr.setNumCreatedFIDsForFileID(CurPPLexer->getFileID(), NumFIDs);
}
bool ExitedFromPredefinesFile = false;
FileID ExitedFID;
if (Callbacks && !isEndOfMacro && CurPPLexer)
if (!isEndOfMacro && CurPPLexer) {
ExitedFID = CurPPLexer->getFileID();
assert(PredefinesFileID.isValid() &&
"HandleEndOfFile is called before PredefinesFileId is set");
ExitedFromPredefinesFile = (PredefinesFileID == ExitedFID);
}
if (LeavingSubmodule) {
// We're done with this submodule.
Module *M = LeaveSubmodule(/*ForPragma*/false);
@ -489,6 +495,11 @@ bool Preprocessor::HandleEndOfFile(Token &Result, bool isEndOfMacro) {
PPCallbacks::ExitFile, FileType, ExitedFID);
}
// Restore conditional stack from the preamble right after exiting from the
// predefines file.
if (ExitedFromPredefinesFile)
replayPreambleConditionalStack();
// Client should lex another token unless we generated an EOM.
return LeavingSubmodule;
}

View File

@ -540,6 +540,8 @@ void Preprocessor::EnterMainSourceFile() {
void Preprocessor::replayPreambleConditionalStack() {
// Restore the conditional stack from the preamble, if there is one.
if (PreambleConditionalStack.isReplaying()) {
assert(CurPPLexer &&
"CurPPLexer is null when calling replayPreambleConditionalStack.");
CurPPLexer->setConditionalLevels(PreambleConditionalStack.getStack());
PreambleConditionalStack.doneReplaying();
}

View File

@ -516,8 +516,6 @@ void Parser::Initialize() {
// Prime the lexer look-ahead.
ConsumeToken();
PP.replayPreambleConditionalStack();
}
void Parser::LateTemplateParserCleanupCallback(void *P) {

View File

@ -5726,6 +5726,53 @@ static void DefineImplicitSpecialMember(Sema &S, CXXMethodDecl *MD,
}
}
/// Determine whether a type is permitted to be passed or returned in
/// registers, per C++ [class.temporary]p3.
static bool computeCanPassInRegisters(Sema &S, CXXRecordDecl *D) {
if (D->isDependentType() || D->isInvalidDecl())
return false;
// Per C++ [class.temporary]p3, the relevant condition is:
// each copy constructor, move constructor, and destructor of X is
// either trivial or deleted, and X has at least one non-deleted copy
// or move constructor
bool HasNonDeletedCopyOrMove = false;
if (D->needsImplicitCopyConstructor() &&
!D->defaultedCopyConstructorIsDeleted()) {
if (!D->hasTrivialCopyConstructor())
return false;
HasNonDeletedCopyOrMove = true;
}
if (S.getLangOpts().CPlusPlus11 && D->needsImplicitMoveConstructor() &&
!D->defaultedMoveConstructorIsDeleted()) {
if (!D->hasTrivialMoveConstructor())
return false;
HasNonDeletedCopyOrMove = true;
}
if (D->needsImplicitDestructor() && !D->defaultedDestructorIsDeleted() &&
!D->hasTrivialDestructor())
return false;
for (const CXXMethodDecl *MD : D->methods()) {
if (MD->isDeleted())
continue;
auto *CD = dyn_cast<CXXConstructorDecl>(MD);
if (CD && CD->isCopyOrMoveConstructor())
HasNonDeletedCopyOrMove = true;
else if (!isa<CXXDestructorDecl>(MD))
continue;
if (!MD->isTrivial())
return false;
}
return HasNonDeletedCopyOrMove;
}
/// \brief Perform semantic checks on a class definition that has been
/// completing, introducing implicitly-declared members, checking for
/// abstract types, etc.
@ -5870,6 +5917,8 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) {
}
checkClassLevelDLLAttribute(Record);
Record->setCanPassInRegisters(computeCanPassInRegisters(*this, Record));
}
/// Look up the special member function that would be called by a special
@ -7496,8 +7545,7 @@ void Sema::ActOnFinishCXXMemberSpecification(Scope* S, SourceLocation RLoc,
reinterpret_cast<Decl**>(FieldCollector->getCurFields()),
FieldCollector->getCurNumFields()), LBrac, RBrac, AttrList);
CheckCompletedCXXClass(
dyn_cast_or_null<CXXRecordDecl>(TagDecl));
CheckCompletedCXXClass(dyn_cast_or_null<CXXRecordDecl>(TagDecl));
}
/// AddImplicitlyDeclaredMembersToClass - Adds any implicitly-declared
@ -11929,8 +11977,10 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor(
Scope *S = getScopeForContext(ClassDecl);
CheckImplicitSpecialMemberDeclaration(S, CopyConstructor);
if (ShouldDeleteSpecialMember(CopyConstructor, CXXCopyConstructor))
if (ShouldDeleteSpecialMember(CopyConstructor, CXXCopyConstructor)) {
ClassDecl->setImplicitCopyConstructorIsDeleted();
SetDeclDeleted(CopyConstructor, ClassLoc);
}
if (S)
PushOnScopeChains(CopyConstructor, S, false);

View File

@ -872,7 +872,7 @@ SelectPropertyForSynthesisFromProtocols(Sema &S, SourceLocation AtLoc,
}
QualType RHSType = S.Context.getCanonicalType(Property->getType());
unsigned OriginalAttributes = Property->getPropertyAttributes();
unsigned OriginalAttributes = Property->getPropertyAttributesAsWritten();
enum MismatchKind {
IncompatibleType = 0,
HasNoExpectedAttribute,
@ -890,7 +890,7 @@ SelectPropertyForSynthesisFromProtocols(Sema &S, SourceLocation AtLoc,
SmallVector<MismatchingProperty, 4> Mismatches;
for (ObjCPropertyDecl *Prop : Properties) {
// Verify the property attributes.
unsigned Attr = Prop->getPropertyAttributes();
unsigned Attr = Prop->getPropertyAttributesAsWritten();
if (Attr != OriginalAttributes) {
auto Diag = [&](bool OriginalHasAttribute, StringRef AttributeName) {
MismatchKind Kind = OriginalHasAttribute ? HasNoExpectedAttribute

View File

@ -1559,9 +1559,11 @@ void ASTDeclReader::ReadCXXDefinitionData(
Data.HasUninitializedFields = Record.readInt();
Data.HasInheritedConstructor = Record.readInt();
Data.HasInheritedAssignment = Record.readInt();
Data.NeedOverloadResolutionForCopyConstructor = Record.readInt();
Data.NeedOverloadResolutionForMoveConstructor = Record.readInt();
Data.NeedOverloadResolutionForMoveAssignment = Record.readInt();
Data.NeedOverloadResolutionForDestructor = Record.readInt();
Data.DefaultedCopyConstructorIsDeleted = Record.readInt();
Data.DefaultedMoveConstructorIsDeleted = Record.readInt();
Data.DefaultedMoveAssignmentIsDeleted = Record.readInt();
Data.DefaultedDestructorIsDeleted = Record.readInt();
@ -1570,6 +1572,7 @@ void ASTDeclReader::ReadCXXDefinitionData(
Data.HasIrrelevantDestructor = Record.readInt();
Data.HasConstexprNonCopyMoveConstructor = Record.readInt();
Data.HasDefaultedDefaultConstructor = Record.readInt();
Data.CanPassInRegisters = Record.readInt();
Data.DefaultedDefaultConstructorIsConstexpr = Record.readInt();
Data.HasConstexprDefaultConstructor = Record.readInt();
Data.HasNonLiteralTypeFieldsOrBases = Record.readInt();
@ -1697,9 +1700,11 @@ void ASTDeclReader::MergeDefinitionData(
MATCH_FIELD(HasUninitializedFields)
MATCH_FIELD(HasInheritedConstructor)
MATCH_FIELD(HasInheritedAssignment)
MATCH_FIELD(NeedOverloadResolutionForCopyConstructor)
MATCH_FIELD(NeedOverloadResolutionForMoveConstructor)
MATCH_FIELD(NeedOverloadResolutionForMoveAssignment)
MATCH_FIELD(NeedOverloadResolutionForDestructor)
MATCH_FIELD(DefaultedCopyConstructorIsDeleted)
MATCH_FIELD(DefaultedMoveConstructorIsDeleted)
MATCH_FIELD(DefaultedMoveAssignmentIsDeleted)
MATCH_FIELD(DefaultedDestructorIsDeleted)
@ -1708,6 +1713,7 @@ void ASTDeclReader::MergeDefinitionData(
MATCH_FIELD(HasIrrelevantDestructor)
OR_FIELD(HasConstexprNonCopyMoveConstructor)
OR_FIELD(HasDefaultedDefaultConstructor)
MATCH_FIELD(CanPassInRegisters)
MATCH_FIELD(DefaultedDefaultConstructorIsConstexpr)
OR_FIELD(HasConstexprDefaultConstructor)
MATCH_FIELD(HasNonLiteralTypeFieldsOrBases)

View File

@ -5874,9 +5874,11 @@ void ASTRecordWriter::AddCXXDefinitionData(const CXXRecordDecl *D) {
Record->push_back(Data.HasUninitializedFields);
Record->push_back(Data.HasInheritedConstructor);
Record->push_back(Data.HasInheritedAssignment);
Record->push_back(Data.NeedOverloadResolutionForCopyConstructor);
Record->push_back(Data.NeedOverloadResolutionForMoveConstructor);
Record->push_back(Data.NeedOverloadResolutionForMoveAssignment);
Record->push_back(Data.NeedOverloadResolutionForDestructor);
Record->push_back(Data.DefaultedCopyConstructorIsDeleted);
Record->push_back(Data.DefaultedMoveConstructorIsDeleted);
Record->push_back(Data.DefaultedMoveAssignmentIsDeleted);
Record->push_back(Data.DefaultedDestructorIsDeleted);
@ -5885,6 +5887,7 @@ void ASTRecordWriter::AddCXXDefinitionData(const CXXRecordDecl *D) {
Record->push_back(Data.HasIrrelevantDestructor);
Record->push_back(Data.HasConstexprNonCopyMoveConstructor);
Record->push_back(Data.HasDefaultedDefaultConstructor);
Record->push_back(Data.CanPassInRegisters);
Record->push_back(Data.DefaultedDefaultConstructorIsConstexpr);
Record->push_back(Data.HasConstexprDefaultConstructor);
Record->push_back(Data.HasNonLiteralTypeFieldsOrBases);

View File

@ -409,6 +409,19 @@ class RegionStoreManager : public StoreManager {
// BindDefault is only used to initialize a region with a default value.
StoreRef BindDefault(Store store, const MemRegion *R, SVal V) override {
// FIXME: The offsets of empty bases can be tricky because of
// of the so called "empty base class optimization".
// If a base class has been optimized out
// we should not try to create a binding, otherwise we should.
// Unfortunately, at the moment ASTRecordLayout doesn't expose
// the actual sizes of the empty bases
// and trying to infer them from offsets/alignments
// seems to be error-prone and non-trivial because of the trailing padding.
// As a temporary mitigation we don't create bindings for empty bases.
if (R->getKind() == MemRegion::CXXBaseObjectRegionKind &&
cast<CXXBaseObjectRegion>(R)->getDecl()->isEmpty())
return StoreRef(store, *this);
RegionBindingsRef B = getRegionBindings(store);
assert(!B.lookup(R, BindingKey::Direct));

View File

@ -704,3 +704,20 @@ namespace PR19579 {
};
}
}
namespace NoCrashOnEmptyBaseOptimization {
struct NonEmptyBase {
int X;
explicit NonEmptyBase(int X) : X(X) {}
};
struct EmptyBase {};
struct S : NonEmptyBase, EmptyBase {
S() : NonEmptyBase(0), EmptyBase() {}
};
void testSCtorNoCrash() {
S s;
}
}

View File

@ -1,5 +1,6 @@
// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown -emit-llvm -o - %s | FileCheck %s
// RUN: %clang_cc1 -std=c++11 -triple x86_64-windows-msvc -emit-llvm -o - %s | FileCheck %s -check-prefix=WIN64
// RUN: %clang_cc1 -std=c++11 -triple x86_64-windows-msvc -emit-llvm -o - %s -fms-compatibility -fms-compatibility-version=18 | FileCheck %s -check-prefix=WIN64 -check-prefix=WIN64-18
// RUN: %clang_cc1 -std=c++11 -triple x86_64-windows-msvc -emit-llvm -o - %s -fms-compatibility -fms-compatibility-version=19 | FileCheck %s -check-prefix=WIN64 -check-prefix=WIN64-19
namespace trivial {
// Trivial structs should be passed directly.
@ -52,12 +53,11 @@ void foo(A);
void bar() {
foo({});
}
// FIXME: The copy ctor is implicitly deleted.
// CHECK-DISABLED-LABEL: define void @_ZN9move_ctor3barEv()
// CHECK-DISABLED: call void @_Z{{.*}}C1Ev(
// CHECK-DISABLED-NOT: call
// CHECK-DISABLED: call void @_ZN9move_ctor3fooENS_1AE(%"struct.move_ctor::A"* %{{.*}})
// CHECK-DISABLED-LABEL: declare void @_ZN9move_ctor3fooENS_1AE(%"struct.move_ctor::A"*)
// CHECK-LABEL: define void @_ZN9move_ctor3barEv()
// CHECK: call void @_Z{{.*}}C1Ev(
// CHECK-NOT: call
// CHECK: call void @_ZN9move_ctor3fooENS_1AE(%"struct.move_ctor::A"* %{{.*}})
// CHECK-LABEL: declare void @_ZN9move_ctor3fooENS_1AE(%"struct.move_ctor::A"*)
// WIN64-LABEL: declare void @"\01?foo@move_ctor@@YAXUA@1@@Z"(%"struct.move_ctor::A"*)
}
@ -73,12 +73,11 @@ void foo(A);
void bar() {
foo({});
}
// FIXME: The copy ctor is deleted.
// CHECK-DISABLED-LABEL: define void @_ZN11all_deleted3barEv()
// CHECK-DISABLED: call void @_Z{{.*}}C1Ev(
// CHECK-DISABLED-NOT: call
// CHECK-DISABLED: call void @_ZN11all_deleted3fooENS_1AE(%"struct.all_deleted::A"* %{{.*}})
// CHECK-DISABLED-LABEL: declare void @_ZN11all_deleted3fooENS_1AE(%"struct.all_deleted::A"*)
// CHECK-LABEL: define void @_ZN11all_deleted3barEv()
// CHECK: call void @_Z{{.*}}C1Ev(
// CHECK-NOT: call
// CHECK: call void @_ZN11all_deleted3fooENS_1AE(%"struct.all_deleted::A"* %{{.*}})
// CHECK-LABEL: declare void @_ZN11all_deleted3fooENS_1AE(%"struct.all_deleted::A"*)
// WIN64-LABEL: declare void @"\01?foo@all_deleted@@YAXUA@1@@Z"(%"struct.all_deleted::A"*)
}
@ -93,14 +92,15 @@ void foo(A);
void bar() {
foo({});
}
// FIXME: The copy and move ctors are implicitly deleted.
// CHECK-DISABLED-LABEL: define void @_ZN18implicitly_deleted3barEv()
// CHECK-DISABLED: call void @_Z{{.*}}C1Ev(
// CHECK-DISABLED-NOT: call
// CHECK-DISABLED: call void @_ZN18implicitly_deleted3fooENS_1AE(%"struct.implicitly_deleted::A"* %{{.*}})
// CHECK-DISABLED-LABEL: declare void @_ZN18implicitly_deleted3fooENS_1AE(%"struct.implicitly_deleted::A"*)
// CHECK-LABEL: define void @_ZN18implicitly_deleted3barEv()
// CHECK: call void @_Z{{.*}}C1Ev(
// CHECK-NOT: call
// CHECK: call void @_ZN18implicitly_deleted3fooENS_1AE(%"struct.implicitly_deleted::A"* %{{.*}})
// CHECK-LABEL: declare void @_ZN18implicitly_deleted3fooENS_1AE(%"struct.implicitly_deleted::A"*)
// WIN64-LABEL: declare void @"\01?foo@implicitly_deleted@@YAXUA@1@@Z"(%"struct.implicitly_deleted::A"*)
// In MSVC 2013, the copy ctor is not deleted by a move assignment. In MSVC 2015, it is.
// WIN64-18-LABEL: declare void @"\01?foo@implicitly_deleted@@YAXUA@1@@Z"(i64
// WIN64-19-LABEL: declare void @"\01?foo@implicitly_deleted@@YAXUA@1@@Z"(%"struct.implicitly_deleted::A"*)
}
namespace one_deleted {
@ -113,12 +113,11 @@ void foo(A);
void bar() {
foo({});
}
// FIXME: The copy constructor is implicitly deleted.
// CHECK-DISABLED-LABEL: define void @_ZN11one_deleted3barEv()
// CHECK-DISABLED: call void @_Z{{.*}}C1Ev(
// CHECK-DISABLED-NOT: call
// CHECK-DISABLED: call void @_ZN11one_deleted3fooENS_1AE(%"struct.one_deleted::A"* %{{.*}})
// CHECK-DISABLED-LABEL: declare void @_ZN11one_deleted3fooENS_1AE(%"struct.one_deleted::A"*)
// CHECK-LABEL: define void @_ZN11one_deleted3barEv()
// CHECK: call void @_Z{{.*}}C1Ev(
// CHECK-NOT: call
// CHECK: call void @_ZN11one_deleted3fooENS_1AE(%"struct.one_deleted::A"* %{{.*}})
// CHECK-LABEL: declare void @_ZN11one_deleted3fooENS_1AE(%"struct.one_deleted::A"*)
// WIN64-LABEL: declare void @"\01?foo@one_deleted@@YAXUA@1@@Z"(%"struct.one_deleted::A"*)
}
@ -195,12 +194,10 @@ void foo(B);
void bar() {
foo({});
}
// FIXME: This class has a non-trivial copy ctor and a trivial copy ctor. It's
// not clear whether we should pass by address or in registers.
// CHECK-DISABLED-LABEL: define void @_ZN14two_copy_ctors3barEv()
// CHECK-DISABLED: call void @_Z{{.*}}C1Ev(
// CHECK-DISABLED: call void @_ZN14two_copy_ctors3fooENS_1BE(%"struct.two_copy_ctors::B"* %{{.*}})
// CHECK-DISABLED-LABEL: declare void @_ZN14two_copy_ctors3fooENS_1BE(%"struct.two_copy_ctors::B"*)
// CHECK-LABEL: define void @_ZN14two_copy_ctors3barEv()
// CHECK: call void @_Z{{.*}}C1Ev(
// CHECK: call void @_ZN14two_copy_ctors3fooENS_1BE(%"struct.two_copy_ctors::B"* %{{.*}})
// CHECK-LABEL: declare void @_ZN14two_copy_ctors3fooENS_1BE(%"struct.two_copy_ctors::B"*)
// WIN64-LABEL: declare void @"\01?foo@two_copy_ctors@@YAXUB@1@@Z"(%"struct.two_copy_ctors::B"*)
}
@ -212,6 +209,7 @@ struct A {
void *p;
};
void *foo(A a) { return a.p; }
// CHECK-LABEL: define i8* @_ZN15definition_only3fooENS_1AE(%"struct.definition_only::A"*
// WIN64-LABEL: define i8* @"\01?foo@definition_only@@YAPEAXUA@1@@Z"(%"struct.definition_only::A"*
}
@ -226,6 +224,7 @@ struct A {
B b;
};
void *foo(A a) { return a.b.p; }
// CHECK-LABEL: define i8* @_ZN17deleted_by_member3fooENS_1AE(%"struct.deleted_by_member::A"*
// WIN64-LABEL: define i8* @"\01?foo@deleted_by_member@@YAPEAXUA@1@@Z"(%"struct.deleted_by_member::A"*
}
@ -239,6 +238,7 @@ struct A : B {
A();
};
void *foo(A a) { return a.p; }
// CHECK-LABEL: define i8* @_ZN15deleted_by_base3fooENS_1AE(%"struct.deleted_by_base::A"*
// WIN64-LABEL: define i8* @"\01?foo@deleted_by_base@@YAPEAXUA@1@@Z"(%"struct.deleted_by_base::A"*
}
@ -253,6 +253,7 @@ struct A {
B b;
};
void *foo(A a) { return a.b.p; }
// CHECK-LABEL: define i8* @_ZN22deleted_by_member_copy3fooENS_1AE(%"struct.deleted_by_member_copy::A"*
// WIN64-LABEL: define i8* @"\01?foo@deleted_by_member_copy@@YAPEAXUA@1@@Z"(%"struct.deleted_by_member_copy::A"*
}
@ -266,6 +267,7 @@ struct A : B {
A();
};
void *foo(A a) { return a.p; }
// CHECK-LABEL: define i8* @_ZN20deleted_by_base_copy3fooENS_1AE(%"struct.deleted_by_base_copy::A"*
// WIN64-LABEL: define i8* @"\01?foo@deleted_by_base_copy@@YAPEAXUA@1@@Z"(%"struct.deleted_by_base_copy::A"*
}
@ -275,6 +277,75 @@ struct A {
A(const A &o) = delete;
void *p;
};
// CHECK-LABEL: define i8* @_ZN15explicit_delete3fooENS_1AE(%"struct.explicit_delete::A"*
// WIN64-LABEL: define i8* @"\01?foo@explicit_delete@@YAPEAXUA@1@@Z"(%"struct.explicit_delete::A"*
void *foo(A a) { return a.p; }
}
namespace implicitly_deleted_copy_ctor {
struct A {
// No move ctor due to copy assignment.
A &operator=(const A&);
// Deleted copy ctor due to rvalue ref member.
int &&ref;
};
// CHECK-LABEL: define {{.*}} @_ZN28implicitly_deleted_copy_ctor3fooENS_1AE(%"struct.implicitly_deleted_copy_ctor::A"*
// WIN64-LABEL: define {{.*}} @"\01?foo@implicitly_deleted_copy_ctor@@YAAEAHUA@1@@Z"(%"struct.implicitly_deleted_copy_ctor::A"*
int &foo(A a) { return a.ref; }
struct B {
// Passed direct: has non-deleted trivial copy ctor.
B &operator=(const B&);
int &ref;
};
int &foo(B b) { return b.ref; }
// CHECK-LABEL: define {{.*}} @_ZN28implicitly_deleted_copy_ctor3fooENS_1BE(i32*
// WIN64-LABEL: define {{.*}} @"\01?foo@implicitly_deleted_copy_ctor@@YAAEAHUB@1@@Z"(i64
struct X { X(const X&); };
struct Y { Y(const Y&) = default; };
union C {
C &operator=(const C&);
// Passed indirect: copy ctor deleted due to variant member with nontrivial copy ctor.
X x;
int n;
};
int foo(C c) { return c.n; }
// CHECK-LABEL: define {{.*}} @_ZN28implicitly_deleted_copy_ctor3fooENS_1CE(%"union.implicitly_deleted_copy_ctor::C"*
// WIN64-LABEL: define {{.*}} @"\01?foo@implicitly_deleted_copy_ctor@@YAHTC@1@@Z"(%"union.implicitly_deleted_copy_ctor::C"*
struct D {
D &operator=(const D&);
// Passed indirect: copy ctor deleted due to variant member with nontrivial copy ctor.
union {
X x;
int n;
};
};
int foo(D d) { return d.n; }
// CHECK-LABEL: define {{.*}} @_ZN28implicitly_deleted_copy_ctor3fooENS_1DE(%"struct.implicitly_deleted_copy_ctor::D"*
// WIN64-LABEL: define {{.*}} @"\01?foo@implicitly_deleted_copy_ctor@@YAHUD@1@@Z"(%"struct.implicitly_deleted_copy_ctor::D"*
union E {
// Passed direct: has non-deleted trivial copy ctor.
E &operator=(const E&);
Y y;
int n;
};
int foo(E e) { return e.n; }
// CHECK-LABEL: define {{.*}} @_ZN28implicitly_deleted_copy_ctor3fooENS_1EE(i32
// WIN64-LABEL: define {{.*}} @"\01?foo@implicitly_deleted_copy_ctor@@YAHTE@1@@Z"(i32
struct F {
// Passed direct: has non-deleted trivial copy ctor.
F &operator=(const F&);
union {
Y y;
int n;
};
};
int foo(F f) { return f.n; }
// CHECK-LABEL: define {{.*}} @_ZN28implicitly_deleted_copy_ctor3fooENS_1FE(i32
// WIN64-LABEL: define {{.*}} @"\01?foo@implicitly_deleted_copy_ctor@@YAHUF@1@@Z"(i32
}

View File

@ -73,6 +73,10 @@
// RUN: FileCheck -check-prefix=ARM64-APPLE %s
// ARM64-APPLE: -munwind-table
// RUN: %clang -target arm64-apple-ios10 -fno-exceptions -### -S %s -arch arm64 2>&1 | \
// RUN: FileCheck -check-prefix=ARM64-APPLE-EXCEP %s
// ARM64-APPLE-EXCEP-NOT: -munwind-table
// RUN: %clang -target armv7k-apple-watchos4.0 -### -S %s -arch armv7k 2>&1 | \
// RUN: FileCheck -check-prefix=ARMV7K-APPLE %s
// ARMV7K-APPLE: -munwind-table

View File

@ -0,0 +1,12 @@
#ifndef HEADER_GUARD
#define FOO int aba;
FOO
#endif
// RUN: env CINDEXTEST_EDITING=1 c-index-test -test-load-source-reparse 5 \
// RUN: local -std=c++14 %s 2>&1 \
// RUN: | FileCheck %s --implicit-check-not "libclang: crash detected" \
// RUN: --implicit-check-not "error:"
// CHECK: macro expansion=FOO:3:9 Extent=[4:1 - 4:4]
// CHECK: VarDecl=aba:4:1 (Definition) Extent=[4:1 - 4:4]

View File

@ -0,0 +1,8 @@
// RUN: env CINDEXTEST_EDITING=1 c-index-test -test-load-source local %s 2>&1 \
// RUN: | FileCheck %s --implicit-check-not "error:"
#ifndef FOO_H
#define FOO_H
void foo();
#endif

View File

@ -225,3 +225,30 @@ __attribute__((objc_root_class))
@implementation TypeVsSetter
@synthesize prop; // expected-note {{property synthesized here}}
@end
@protocol AutoStrongProp
@property (nonatomic, readonly) NSObject *prop;
@end
@protocol AutoStrongProp_Internal <AutoStrongProp>
// This property gets the 'strong' attribute automatically.
@property (nonatomic, readwrite) NSObject *prop;
@end
@interface SynthesizeWithImplicitStrongNoError : NSObject <AutoStrongProp>
@end
@interface SynthesizeWithImplicitStrongNoError () <AutoStrongProp_Internal>
@end
@implementation SynthesizeWithImplicitStrongNoError
// no error, 'strong' is implicit in the 'readwrite' property.
@synthesize prop = _prop;
@end

View File

@ -1108,26 +1108,35 @@ TEST(ConstructorDeclaration, IsExplicit) {
}
TEST(ConstructorDeclaration, Kinds) {
EXPECT_TRUE(matches("struct S { S(); };",
cxxConstructorDecl(isDefaultConstructor())));
EXPECT_TRUE(notMatches("struct S { S(); };",
cxxConstructorDecl(isCopyConstructor())));
EXPECT_TRUE(notMatches("struct S { S(); };",
cxxConstructorDecl(isMoveConstructor())));
EXPECT_TRUE(matches(
"struct S { S(); };",
cxxConstructorDecl(isDefaultConstructor(), unless(isImplicit()))));
EXPECT_TRUE(notMatches(
"struct S { S(); };",
cxxConstructorDecl(isCopyConstructor(), unless(isImplicit()))));
EXPECT_TRUE(notMatches(
"struct S { S(); };",
cxxConstructorDecl(isMoveConstructor(), unless(isImplicit()))));
EXPECT_TRUE(notMatches("struct S { S(const S&); };",
cxxConstructorDecl(isDefaultConstructor())));
EXPECT_TRUE(matches("struct S { S(const S&); };",
cxxConstructorDecl(isCopyConstructor())));
EXPECT_TRUE(notMatches("struct S { S(const S&); };",
cxxConstructorDecl(isMoveConstructor())));
EXPECT_TRUE(notMatches(
"struct S { S(const S&); };",
cxxConstructorDecl(isDefaultConstructor(), unless(isImplicit()))));
EXPECT_TRUE(matches(
"struct S { S(const S&); };",
cxxConstructorDecl(isCopyConstructor(), unless(isImplicit()))));
EXPECT_TRUE(notMatches(
"struct S { S(const S&); };",
cxxConstructorDecl(isMoveConstructor(), unless(isImplicit()))));
EXPECT_TRUE(notMatches("struct S { S(S&&); };",
cxxConstructorDecl(isDefaultConstructor())));
EXPECT_TRUE(notMatches("struct S { S(S&&); };",
cxxConstructorDecl(isCopyConstructor())));
EXPECT_TRUE(matches("struct S { S(S&&); };",
cxxConstructorDecl(isMoveConstructor())));
EXPECT_TRUE(notMatches(
"struct S { S(S&&); };",
cxxConstructorDecl(isDefaultConstructor(), unless(isImplicit()))));
EXPECT_TRUE(notMatches(
"struct S { S(S&&); };",
cxxConstructorDecl(isCopyConstructor(), unless(isImplicit()))));
EXPECT_TRUE(matches(
"struct S { S(S&&); };",
cxxConstructorDecl(isMoveConstructor(), unless(isImplicit()))));
}
TEST(ConstructorDeclaration, IsUserProvided) {

View File

@ -2267,6 +2267,13 @@ TEST_F(FormatTestComments, AlignTrailingComments) {
"int k; // line longg long",
getLLVMStyleWithColumns(20)));
// Always align if ColumnLimit = 0
EXPECT_EQ("int i, j; // line 1\n"
"int k; // line longg long",
format("int i, j; // line 1\n"
"int k; // line longg long",
getLLVMStyleWithColumns(0)));
// Align comment line sections aligned with the next token with the next
// token.
EXPECT_EQ("class A {\n"