24 changes: 24 additions & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4016,6 +4016,30 @@ Note that the `size` argument must be a compile time constant.
Note that this intrinsic cannot yet be called in a ``constexpr`` context.
``__is_bitwise_cloneable``
--------------------------
A type trait is used to check whether a type can be safely copied by memcpy.
**Syntax**:
.. code-block:: c++
bool __is_bitwise_cloneable(Type)
**Description**:
Objects of bitwise cloneable types can be bitwise copied by memcpy/memmove. The
Clang compiler warrants that this behavior is well defined, and won't be
broken by compiler optimizations and sanitizers.
For implicit-lifetime types, the lifetime of the new object is implicitly
started after the copy. For other types (e.g., classes with virtual methods),
the lifetime isn't started, and using the object results in undefined behavior
according to the C++ Standard.
This builtin can be used in constant expressions.
Atomic Min/Max builtins with memory ordering
--------------------------------------------
Expand Down
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,9 @@ Non-comprehensive list of changes in this release
``-Winvalid-constexpr`` is not enabled for the function definition, which
should result in mild compile-time performance improvements.

- Added ``__is_bitwise_cloneable`` which is used to check whether a type
can be safely copied by memcpy/memmove.

New Compiler Flags
------------------
- ``-fsanitize=implicit-bitfield-conversion`` checks implicit truncation and
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/AST/OpenACCClause.h
Original file line number Diff line number Diff line change
Expand Up @@ -867,7 +867,7 @@ template <class Impl> class OpenACCClauseVisitor {
case OpenACCClauseKind::CLAUSE_NAME: \
Visit##CLAUSE_NAME##Clause(*cast<OpenACC##CLAUSE_NAME##Clause>(C)); \
return;
#define CLAUSE_ALIAS(ALIAS_NAME, CLAUSE_NAME) \
#define CLAUSE_ALIAS(ALIAS_NAME, CLAUSE_NAME, DEPRECATED) \
case OpenACCClauseKind::ALIAS_NAME: \
Visit##CLAUSE_NAME##Clause(*cast<OpenACC##CLAUSE_NAME##Clause>(C)); \
return;
Expand Down
14 changes: 14 additions & 0 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -1120,6 +1120,20 @@ class QualType {
/// Return true if this is a trivially copyable type (C++0x [basic.types]p9)
bool isTriviallyCopyableType(const ASTContext &Context) const;

/// Return true if the type is safe to bitwise copy using memcpy/memmove.
///
/// This is an extension in clang: bitwise cloneable types act as trivially
/// copyable types, meaning their underlying bytes can be safely copied by
/// memcpy or memmove. After the copy, the destination object has the same
/// object representation.
///
/// However, there are cases where it is not safe to copy:
/// - When sanitizers, such as AddressSanitizer, add padding with poison,
/// which can cause issues if those poisoned padding bits are accessed.
/// - Types with Objective-C lifetimes, where specific runtime
/// semantics may not be preserved during a bitwise copy.
bool isBitwiseCloneableType(const ASTContext &Context) const;

/// Return true if this is a trivially copyable type
bool isTriviallyCopyConstructibleType(const ASTContext &Context) const;

Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/BuiltinsAMDGPU.def
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ TARGET_BUILTIN(__builtin_amdgcn_flat_atomic_fadd_v2bf16, "V2sV2s*0V2s", "t", "at
TARGET_BUILTIN(__builtin_amdgcn_global_atomic_fadd_v2bf16, "V2sV2s*1V2s", "t", "atomic-global-pk-add-bf16-inst")
TARGET_BUILTIN(__builtin_amdgcn_ds_atomic_fadd_v2bf16, "V2sV2s*3V2s", "t", "atomic-ds-pk-add-16-insts")
TARGET_BUILTIN(__builtin_amdgcn_ds_atomic_fadd_v2f16, "V2hV2h*3V2h", "t", "atomic-ds-pk-add-16-insts")
TARGET_BUILTIN(__builtin_amdgcn_global_load_lds, "vv*1v*3UiiUi", "t", "gfx940-insts")
TARGET_BUILTIN(__builtin_amdgcn_global_load_lds, "vv*1v*3IUiIiIUi", "t", "gfx940-insts")

//===----------------------------------------------------------------------===//
// Deep learning builtins.
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/Cuda.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ enum class CudaArch {
GFX1103,
GFX1150,
GFX1151,
GFX1152,
GFX12_GENERIC,
GFX1200,
GFX1201,
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -10082,6 +10082,12 @@ def warn_new_dangling_initializer_list : Warning<
"the allocated initializer list}0 "
"will be destroyed at the end of the full-expression">,
InGroup<DanglingInitializerList>;
def warn_unsupported_lifetime_extension : Warning<
"lifetime extension of "
"%select{temporary|backing array of initializer list}0 created "
"by aggregate initialization using a default member initializer "
"is not yet supported; lifetime of %select{temporary|backing array}0 "
"will end at the end of the full-expression">, InGroup<Dangling>;

// For non-floating point, expressions of the form x == x or x != x
// should result in a warning, since these always evaluate to a constant.
Expand Down
22 changes: 11 additions & 11 deletions clang/include/clang/Basic/OpenACCClauses.def
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,31 @@
//
// VISIT_CLAUSE(CLAUSE_NAME)
//
// CLAUSE_ALIAS(ALIAS_NAME, CLAUSE_NAME)
// CLAUSE_ALIAS(ALIAS_NAME, CLAUSE_NAME, DEPRECATED)

#ifndef CLAUSE_ALIAS
#define CLAUSE_ALIAS(ALIAS_NAME, CLAUSE_NAME)
#define CLAUSE_ALIAS(ALIAS_NAME, CLAUSE_NAME, false)
#endif

VISIT_CLAUSE(Auto)
VISIT_CLAUSE(Async)
VISIT_CLAUSE(Attach)
VISIT_CLAUSE(Copy)
CLAUSE_ALIAS(PCopy, Copy)
CLAUSE_ALIAS(PresentOrCopy, Copy)
CLAUSE_ALIAS(PCopy, Copy, true)
CLAUSE_ALIAS(PresentOrCopy, Copy, true)
VISIT_CLAUSE(CopyIn)
CLAUSE_ALIAS(PCopyIn, CopyIn)
CLAUSE_ALIAS(PresentOrCopyIn, CopyIn)
CLAUSE_ALIAS(PCopyIn, CopyIn, true)
CLAUSE_ALIAS(PresentOrCopyIn, CopyIn, true)
VISIT_CLAUSE(CopyOut)
CLAUSE_ALIAS(PCopyOut, CopyOut)
CLAUSE_ALIAS(PresentOrCopyOut, CopyOut)
CLAUSE_ALIAS(PCopyOut, CopyOut, true)
CLAUSE_ALIAS(PresentOrCopyOut, CopyOut, true)
VISIT_CLAUSE(Create)
CLAUSE_ALIAS(PCreate, Create)
CLAUSE_ALIAS(PresentOrCreate, Create)
CLAUSE_ALIAS(PCreate, Create, true)
CLAUSE_ALIAS(PresentOrCreate, Create, true)
VISIT_CLAUSE(Default)
VISIT_CLAUSE(DevicePtr)
VISIT_CLAUSE(DeviceType)
CLAUSE_ALIAS(DType, DeviceType)
CLAUSE_ALIAS(DType, DeviceType, false)
VISIT_CLAUSE(FirstPrivate)
VISIT_CLAUSE(If)
VISIT_CLAUSE(Independent)
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,8 @@ TYPE_TRAIT_2(__reference_converts_from_temporary, ReferenceConvertsFromTemporary
// is not exposed to users.
TYPE_TRAIT_2(/*EmptySpellingName*/, IsDeducible, KEYCXX)

TYPE_TRAIT_1(__is_bitwise_cloneable, IsBitwiseCloneable, KEYALL)

// Embarcadero Expression Traits
EXPRESSION_TRAIT(__is_lvalue_expr, IsLValueExpr, KEYCXX)
EXPRESSION_TRAIT(__is_rvalue_expr, IsRValueExpr, KEYCXX)
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/Basic/riscv_vector.td
Original file line number Diff line number Diff line change
Expand Up @@ -2637,7 +2637,8 @@ let UnMaskedPolicyScheme = HasPassthruOperand in {
defm vbrev : RVVOutBuiltinSetZvbb;
defm vclz : RVVOutBuiltinSetZvbb;
defm vctz : RVVOutBuiltinSetZvbb;
defm vcpopv : RVVOutBuiltinSetZvbb;
let IRName = "vcpopv", MaskedIRName = "vcpopv_mask" in
defm vcpop : RVVOutBuiltinSetZvbb;
let OverloadedName = "vwsll" in
defm vwsll : RVVSignedWidenBinBuiltinSetVwsll;
}
Expand Down
3 changes: 1 addition & 2 deletions clang/include/clang/Lex/DependencyDirectivesScanner.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
#ifndef LLVM_CLANG_LEX_DEPENDENCYDIRECTIVESSCANNER_H
#define LLVM_CLANG_LEX_DEPENDENCYDIRECTIVESSCANNER_H

#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/ArrayRef.h"

Expand Down Expand Up @@ -118,7 +117,7 @@ struct Directive {
bool scanSourceForDependencyDirectives(
StringRef Input, SmallVectorImpl<dependency_directives_scan::Token> &Tokens,
SmallVectorImpl<dependency_directives_scan::Directive> &Directives,
const LangOptions &LangOpts, DiagnosticsEngine *Diags = nullptr,
DiagnosticsEngine *Diags = nullptr,
SourceLocation InputSourceLoc = SourceLocation());

/// Print the previously scanned dependency directives as minimized source text.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,8 +363,7 @@ class DependencyScanningWorkerFilesystem
///
/// Returns true if the directive tokens are populated for this file entry,
/// false if not (i.e. this entry is not a file or its scan fails).
bool ensureDirectiveTokensArePopulated(EntryRef Entry,
const LangOptions &LangOpts);
bool ensureDirectiveTokensArePopulated(EntryRef Entry);

/// Check whether \p Path exists. By default checks cached result of \c
/// status(), and falls back on FS if unable to do so.
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ add_clang_library(clangAST
Interp/Record.cpp
Interp/Source.cpp
Interp/State.cpp
Interp/MemberPointer.cpp
Interp/InterpShared.cpp
ItaniumCXXABI.cpp
ItaniumMangle.cpp
Expand Down
102 changes: 92 additions & 10 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,35 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
return this->emitMemcpy(CE);
}

case CK_DerivedToBaseMemberPointer: {
assert(classifyPrim(CE->getType()) == PT_MemberPtr);
assert(classifyPrim(SubExpr->getType()) == PT_MemberPtr);
const auto *FromMP = SubExpr->getType()->getAs<MemberPointerType>();
const auto *ToMP = CE->getType()->getAs<MemberPointerType>();

unsigned DerivedOffset = collectBaseOffset(QualType(ToMP->getClass(), 0),
QualType(FromMP->getClass(), 0));

if (!this->visit(SubExpr))
return false;

return this->emitGetMemberPtrBasePop(DerivedOffset, CE);
}

case CK_BaseToDerivedMemberPointer: {
assert(classifyPrim(CE) == PT_MemberPtr);
assert(classifyPrim(SubExpr) == PT_MemberPtr);
const auto *FromMP = SubExpr->getType()->getAs<MemberPointerType>();
const auto *ToMP = CE->getType()->getAs<MemberPointerType>();

unsigned DerivedOffset = collectBaseOffset(QualType(FromMP->getClass(), 0),
QualType(ToMP->getClass(), 0));

if (!this->visit(SubExpr))
return false;
return this->emitGetMemberPtrBasePop(-DerivedOffset, CE);
}

case CK_UncheckedDerivedToBase:
case CK_DerivedToBase: {
if (!this->visit(SubExpr))
Expand Down Expand Up @@ -187,7 +216,8 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
return this->emitCastFloatingIntegral(*ToT, CE);
}

case CK_NullToPointer: {
case CK_NullToPointer:
case CK_NullToMemberPointer: {
if (DiscardResult)
return true;

Expand Down Expand Up @@ -326,7 +356,8 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
return this->emitCast(*FromT, *ToT, CE);
}

case CK_PointerToBoolean: {
case CK_PointerToBoolean:
case CK_MemberPointerToBoolean: {
PrimType PtrT = classifyPrim(SubExpr->getType());

// Just emit p != nullptr for this.
Expand Down Expand Up @@ -534,8 +565,23 @@ bool ByteCodeExprGen<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
BO->isComparisonOp())
return this->emitComplexComparison(LHS, RHS, BO);

if (BO->isPtrMemOp())
return this->visit(RHS);
if (BO->isPtrMemOp()) {
if (!this->visit(LHS))
return false;

if (!this->visit(RHS))
return false;

if (!this->emitToMemberPtr(BO))
return false;

if (classifyPrim(BO) == PT_MemberPtr)
return true;

if (!this->emitCastMemberPtrPtr(BO))
return false;
return DiscardResult ? this->emitPopPtr(BO) : true;
}

// Typecheck the args.
std::optional<PrimType> LT = classify(LHS->getType());
Expand Down Expand Up @@ -2773,6 +2819,8 @@ bool ByteCodeExprGen<Emitter>::visitZeroInitializer(PrimType T, QualType QT,
return this->emitNullPtr(nullptr, E);
case PT_FnPtr:
return this->emitNullFnPtr(nullptr, E);
case PT_MemberPtr:
return this->emitNullMemberPtr(nullptr, E);
case PT_Float: {
return this->emitConstFloat(APFloat::getZero(Ctx.getFloatSemantics(QT)), E);
}
Expand Down Expand Up @@ -2875,6 +2923,7 @@ bool ByteCodeExprGen<Emitter>::emitConst(T Value, PrimType Ty, const Expr *E) {
return this->emitConstBool(Value, E);
case PT_Ptr:
case PT_FnPtr:
case PT_MemberPtr:
case PT_Float:
case PT_IntAP:
case PT_IntAPS:
Expand Down Expand Up @@ -3188,7 +3237,7 @@ bool ByteCodeExprGen<Emitter>::visitAPValueInitializer(const APValue &Val,
const APValue &F = Val.getStructField(I);
const Record::Field *RF = R->getField(I);

if (F.isInt()) {
if (F.isInt() || F.isLValue()) {
PrimType T = classifyPrim(RF->Decl->getType());
if (!this->visitAPValue(F, T, E))
return false;
Expand Down Expand Up @@ -3308,10 +3357,27 @@ bool ByteCodeExprGen<Emitter>::VisitCallExpr(const CallExpr *E) {
}
}

std::optional<unsigned> CalleeOffset;
// Add the (optional, implicit) This pointer.
if (const auto *MC = dyn_cast<CXXMemberCallExpr>(E)) {
if (!this->visit(MC->getImplicitObjectArgument()))
if (!FuncDecl && classifyPrim(E->getCallee()) == PT_MemberPtr) {
// If we end up creating a CallPtr op for this, we need the base of the
// member pointer as the instance pointer, and later extract the function
// decl as the function pointer.
const Expr *Callee = E->getCallee();
CalleeOffset =
this->allocateLocalPrimitive(Callee, PT_MemberPtr, true, false);
if (!this->visit(Callee))
return false;
if (!this->emitSetLocal(PT_MemberPtr, *CalleeOffset, E))
return false;
if (!this->emitGetLocal(PT_MemberPtr, *CalleeOffset, E))
return false;
if (!this->emitGetMemberPtrBase(E))
return false;
} else if (!this->visit(MC->getImplicitObjectArgument())) {
return false;
}
}

llvm::BitVector NonNullArgs = collectNonNullArgs(FuncDecl, Args);
Expand Down Expand Up @@ -3380,11 +3446,22 @@ bool ByteCodeExprGen<Emitter>::VisitCallExpr(const CallExpr *E) {
for (unsigned I = 0, N = E->getNumArgs(); I != N; ++I)
ArgSize += align(primSize(classify(E->getArg(I)).value_or(PT_Ptr)));

if (!this->visit(E->getCallee()))
return false;
// Get the callee, either from a member pointer saved in CalleeOffset,
// or by just visiting the Callee expr.
if (CalleeOffset) {
if (!this->emitGetLocal(PT_MemberPtr, *CalleeOffset, E))
return false;
if (!this->emitGetMemberPtrDecl(E))
return false;
if (!this->emitCallPtr(ArgSize, E, E))
return false;
} else {
if (!this->visit(E->getCallee()))
return false;

if (!this->emitCallPtr(ArgSize, E, E))
return false;
if (!this->emitCallPtr(ArgSize, E, E))
return false;
}
}

// Cleanup for discarded return values.
Expand Down Expand Up @@ -3623,6 +3700,11 @@ bool ByteCodeExprGen<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
return false;
return DiscardResult ? this->emitPop(*T, E) : true;
case UO_AddrOf: // &x
if (E->getType()->isMemberPointerType()) {
// C++11 [expr.unary.op]p3 has very strict rules on how the address of a
// member can be formed.
return this->emitGetMemberPtr(cast<DeclRefExpr>(SubExpr)->getDecl(), E);
}
// We should already have a pointer when we get here.
return this->delegate(SubExpr);
case UO_Deref: // *x
Expand Down
15 changes: 9 additions & 6 deletions clang/lib/AST/Interp/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,12 @@ std::optional<PrimType> Context::classify(QualType T) const {
if (T->isFloatingType())
return PT_Float;

if (T->isSpecificBuiltinType(BuiltinType::BoundMember) ||
T->isMemberPointerType())
return PT_MemberPtr;

if (T->isFunctionPointerType() || T->isFunctionReferenceType() ||
T->isFunctionType() || T->isSpecificBuiltinType(BuiltinType::BoundMember))
T->isFunctionType())
return PT_FnPtr;

if (T->isReferenceType() || T->isPointerType() ||
Expand All @@ -177,9 +181,6 @@ std::optional<PrimType> Context::classify(QualType T) const {
if (const auto *DT = dyn_cast<DecltypeType>(T))
return classify(DT->getUnderlyingType());

if (const auto *DT = dyn_cast<MemberPointerType>(T))
return classify(DT->getPointeeType());

return std::nullopt;
}

Expand Down Expand Up @@ -292,10 +293,12 @@ unsigned Context::collectBaseOffset(const RecordDecl *BaseDecl,
}
if (CurDecl == FinalDecl)
break;

// break;
}

assert(OffsetSum > 0);
return OffsetSum;
}

const Record *Context::getRecord(const RecordDecl *D) const {
return P->getOrCreateRecord(D);
}
2 changes: 2 additions & 0 deletions clang/lib/AST/Interp/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ class Context final {
unsigned collectBaseOffset(const RecordDecl *BaseDecl,
const RecordDecl *DerivedDecl) const;

const Record *getRecord(const RecordDecl *D) const;

private:
/// Runs a function.
bool Run(State &Parent, const Function *Func, APValue &Result);
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/Descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "Floating.h"
#include "FunctionPointer.h"
#include "IntegralAP.h"
#include "MemberPointer.h"
#include "Pointer.h"
#include "PrimType.h"
#include "Record.h"
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/Interp/Disasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "Integral.h"
#include "IntegralAP.h"
#include "InterpFrame.h"
#include "MemberPointer.h"
#include "Opcode.h"
#include "PrimType.h"
#include "Program.h"
Expand Down Expand Up @@ -122,6 +123,8 @@ static const char *primTypeToString(PrimType T) {
return "Ptr";
case PT_FnPtr:
return "FnPtr";
case PT_MemberPtr:
return "MemberPtr";
}
llvm_unreachable("Unhandled PrimType");
}
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/AST/Interp/Function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ SourceInfo Function::getSource(CodePtr PC) const {
unsigned Offset = PC - getCodeBegin();
using Elem = std::pair<unsigned, SourceInfo>;
auto It = llvm::lower_bound(SrcMap, Elem{Offset, {}}, llvm::less_first());
assert(It != SrcMap.end());
if (It == SrcMap.end())
return SrcMap.back().second;
return It->second;
}

Expand Down
39 changes: 31 additions & 8 deletions clang/lib/AST/Interp/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,26 @@ bool CheckSubobject(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
return false;
}

bool CheckDowncast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
uint32_t Offset) {
uint32_t MinOffset = Ptr.getDeclDesc()->getMetadataSize();
uint32_t PtrOffset = Ptr.getByteOffset();

// We subtract Offset from PtrOffset. The result must be at least
// MinOffset.
if (Offset < PtrOffset && (PtrOffset - Offset) >= MinOffset)
return true;

const auto *E = cast<CastExpr>(S.Current->getExpr(OpPC));
QualType TargetQT = E->getType()->getPointeeType();
QualType MostDerivedQT = Ptr.getDeclPtr().getType();

S.CCEDiag(E, diag::note_constexpr_invalid_downcast)
<< MostDerivedQT << TargetQT;

return false;
}

bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
assert(Ptr.isLive() && "Pointer is not live");
if (!Ptr.isConst())
Expand Down Expand Up @@ -493,10 +513,12 @@ bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
if (!CheckLive(S, OpPC, Ptr, AK_MemberCall))
return false;
if (!CheckExtern(S, OpPC, Ptr))
return false;
if (!CheckRange(S, OpPC, Ptr, AK_MemberCall))
return false;
if (!Ptr.isDummy()) {
if (!CheckExtern(S, OpPC, Ptr))
return false;
if (!CheckRange(S, OpPC, Ptr, AK_MemberCall))
return false;
}
return true;
}

Expand All @@ -516,7 +538,7 @@ bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
return false;
}

if (!F->isConstexpr()) {
if (!F->isConstexpr() || !F->hasBody()) {
const SourceLocation &Loc = S.Current->getLocation(OpPC);
if (S.getLangOpts().CPlusPlus11) {
const FunctionDecl *DiagDecl = F->getDecl();
Expand Down Expand Up @@ -550,9 +572,10 @@ bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
S.checkingPotentialConstantExpression())
return false;

// If the declaration is defined _and_ declared 'constexpr', the below
// diagnostic doesn't add anything useful.
if (DiagDecl->isDefined() && DiagDecl->isConstexpr())
// If the declaration is defined, declared 'constexpr' _and_ has a body,
// the below diagnostic doesn't add anything useful.
if (DiagDecl->isDefined() && DiagDecl->isConstexpr() &&
DiagDecl->hasBody())
return false;

S.FFDiag(Loc, diag::note_constexpr_invalid_function, 1)
Expand Down
107 changes: 104 additions & 3 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "InterpFrame.h"
#include "InterpStack.h"
#include "InterpState.h"
#include "MemberPointer.h"
#include "Opcode.h"
#include "PrimType.h"
#include "Program.h"
Expand Down Expand Up @@ -75,6 +76,11 @@ bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
bool CheckSubobject(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
CheckSubobjectKind CSK);

/// Checks if the dowcast using the given offset is possible with the given
/// pointer.
bool CheckDowncast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
uint32_t Offset);

/// Checks if a pointer points to const storage.
bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr);

Expand Down Expand Up @@ -725,6 +731,9 @@ using CompareFn = llvm::function_ref<bool(ComparisonCategoryResult)>;

template <typename T>
bool CmpHelper(InterpState &S, CodePtr OpPC, CompareFn Fn) {
assert((!std::is_same_v<T, MemberPointer>) &&
"Non-equality comparisons on member pointer types should already be "
"rejected in Sema.");
using BoolT = PrimConv<PT_Bool>::T;
const T &RHS = S.Stk.pop<T>();
const T &LHS = S.Stk.pop<T>();
Expand Down Expand Up @@ -834,6 +843,47 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
}
}

template <>
inline bool CmpHelperEQ<MemberPointer>(InterpState &S, CodePtr OpPC,
CompareFn Fn) {
const auto &RHS = S.Stk.pop<MemberPointer>();
const auto &LHS = S.Stk.pop<MemberPointer>();

// If either operand is a pointer to a weak function, the comparison is not
// constant.
for (const auto &MP : {LHS, RHS}) {
if (const CXXMethodDecl *MD = MP.getMemberFunction(); MD && MD->isWeak()) {
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.FFDiag(Loc, diag::note_constexpr_mem_pointer_weak_comparison) << MD;
return false;
}
}

// C++11 [expr.eq]p2:
// If both operands are null, they compare equal. Otherwise if only one is
// null, they compare unequal.
if (LHS.isZero() && RHS.isZero()) {
S.Stk.push<Boolean>(Fn(ComparisonCategoryResult::Equal));
return true;
}
if (LHS.isZero() || RHS.isZero()) {
S.Stk.push<Boolean>(Fn(ComparisonCategoryResult::Unordered));
return true;
}

// We cannot compare against virtual declarations at compile time.
for (const auto &MP : {LHS, RHS}) {
if (const CXXMethodDecl *MD = MP.getMemberFunction();
MD && MD->isVirtual()) {
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.CCEDiag(Loc, diag::note_constexpr_compare_virtual_mem_ptr) << MD;
}
}

S.Stk.push<Boolean>(Boolean::from(Fn(LHS.compare(RHS))));
return true;
}

template <PrimType Name, class T = typename PrimConv<Name>::T>
bool EQ(InterpState &S, CodePtr OpPC) {
return CmpHelperEQ<T>(S, OpPC, [](ComparisonCategoryResult R) {
Expand Down Expand Up @@ -1300,6 +1350,9 @@ inline bool GetPtrDerivedPop(InterpState &S, CodePtr OpPC, uint32_t Off) {
return false;
if (!CheckSubobject(S, OpPC, Ptr, CSK_Derived))
return false;
if (!CheckDowncast(S, OpPC, Ptr, Off))
return false;

S.Stk.push<Pointer>(Ptr.atFieldSub(Off));
return true;
}
Expand All @@ -1324,6 +1377,12 @@ inline bool GetPtrBasePop(InterpState &S, CodePtr OpPC, uint32_t Off) {
return true;
}

inline bool GetMemberPtrBasePop(InterpState &S, CodePtr OpPC, int32_t Off) {
const auto &Ptr = S.Stk.pop<MemberPointer>();
S.Stk.push<MemberPointer>(Ptr.atInstanceBase(Off));
return true;
}

inline bool GetPtrThisBase(InterpState &S, CodePtr OpPC, uint32_t Off) {
if (S.checkingPotentialConstantExpression())
return false;
Expand Down Expand Up @@ -1532,6 +1591,24 @@ inline bool Memcpy(InterpState &S, CodePtr OpPC) {
return DoMemcpy(S, OpPC, Src, Dest);
}

inline bool ToMemberPtr(InterpState &S, CodePtr OpPC) {
const auto &Member = S.Stk.pop<MemberPointer>();
const auto &Base = S.Stk.pop<Pointer>();

S.Stk.push<MemberPointer>(Member.takeInstance(Base));
return true;
}

inline bool CastMemberPtrPtr(InterpState &S, CodePtr OpPC) {
const auto &MP = S.Stk.pop<MemberPointer>();

if (std::optional<Pointer> Ptr = MP.toPointer(S.Ctx)) {
S.Stk.push<Pointer>(*Ptr);
return true;
}
return false;
}

//===----------------------------------------------------------------------===//
// AddOffset, SubOffset
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -1696,8 +1773,10 @@ inline bool SubPtr(InterpState &S, CodePtr OpPC) {
return true;
}

T A = T::from(LHS.getIndex());
T B = T::from(RHS.getIndex());
T A = LHS.isElementPastEnd() ? T::from(LHS.getNumElems())
: T::from(LHS.getIndex());
T B = RHS.isElementPastEnd() ? T::from(RHS.getNumElems())
: T::from(RHS.getIndex());
return AddSubMulHelper<T, T::sub, std::minus>(S, OpPC, A.bitWidth(), A, B);
}

Expand Down Expand Up @@ -2115,7 +2194,7 @@ inline bool ArrayDecay(InterpState &S, CodePtr OpPC) {
if (!CheckRange(S, OpPC, Ptr, CSK_ArrayToPointer))
return false;

if (!Ptr.isUnknownSizeArray() || Ptr.isDummy()) {
if (Ptr.isRoot() || !Ptr.isUnknownSizeArray() || Ptr.isDummy()) {
S.Stk.push<Pointer>(Ptr.atIndex(0));
return true;
}
Expand Down Expand Up @@ -2329,6 +2408,28 @@ inline bool GetIntPtr(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
return true;
}

inline bool GetMemberPtr(InterpState &S, CodePtr OpPC, const Decl *D) {
S.Stk.push<MemberPointer>(D);
return true;
}

inline bool GetMemberPtrBase(InterpState &S, CodePtr OpPC) {
const auto &MP = S.Stk.pop<MemberPointer>();

S.Stk.push<Pointer>(MP.getBase());
return true;
}

inline bool GetMemberPtrDecl(InterpState &S, CodePtr OpPC) {
const auto &MP = S.Stk.pop<MemberPointer>();

const auto *FD = cast<FunctionDecl>(MP.getDecl());
const auto *Func = S.getContext().getOrCreateFunction(FD);

S.Stk.push<FunctionPointer>(Func);
return true;
}

/// Just emit a diagnostic. The expression that caused emission of this
/// op is not valid in a constant context.
inline bool Invalid(InterpState &S, CodePtr OpPC) {
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/InterpFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "Function.h"
#include "InterpStack.h"
#include "InterpState.h"
#include "MemberPointer.h"
#include "Pointer.h"
#include "PrimType.h"
#include "Program.h"
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/InterpStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "Boolean.h"
#include "Floating.h"
#include "Integral.h"
#include "MemberPointer.h"
#include "Pointer.h"
#include <cassert>
#include <cstdlib>
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/Interp/InterpStack.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include "FunctionPointer.h"
#include "IntegralAP.h"
#include "MemberPointer.h"
#include "PrimType.h"
#include <memory>
#include <vector>
Expand Down Expand Up @@ -188,6 +189,8 @@ class InterpStack final {
return PT_IntAP;
else if constexpr (std::is_same_v<T, IntegralAP<false>>)
return PT_IntAP;
else if constexpr (std::is_same_v<T, MemberPointer>)
return PT_MemberPtr;

llvm_unreachable("unknown type push()'ed into InterpStack");
}
Expand Down
76 changes: 76 additions & 0 deletions clang/lib/AST/Interp/MemberPointer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//===------------------------- MemberPointer.cpp ----------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "MemberPointer.h"
#include "Context.h"
#include "FunctionPointer.h"
#include "Program.h"
#include "Record.h"

namespace clang {
namespace interp {

std::optional<Pointer> MemberPointer::toPointer(const Context &Ctx) const {
if (!Dcl || isa<FunctionDecl>(Dcl))
return Base;
const FieldDecl *FD = cast<FieldDecl>(Dcl);
assert(FD);

if (!Base.isBlockPointer())
return std::nullopt;

Pointer CastedBase =
(PtrOffset < 0 ? Base.atField(-PtrOffset) : Base.atFieldSub(PtrOffset));

const Record *BaseRecord = CastedBase.getRecord();
if (!BaseRecord)
return std::nullopt;

assert(BaseRecord);
if (FD->getParent() == BaseRecord->getDecl())
return CastedBase.atField(BaseRecord->getField(FD)->Offset);

const RecordDecl *FieldParent = FD->getParent();
const Record *FieldRecord = Ctx.getRecord(FieldParent);

unsigned Offset = 0;
Offset += FieldRecord->getField(FD)->Offset;
Offset += CastedBase.block()->getDescriptor()->getMetadataSize();

if (Offset > CastedBase.block()->getSize())
return std::nullopt;

if (const RecordDecl *BaseDecl = Base.getDeclPtr().getRecord()->getDecl();
BaseDecl != FieldParent)
Offset += Ctx.collectBaseOffset(FieldParent, BaseDecl);

if (Offset > CastedBase.block()->getSize())
return std::nullopt;

assert(Offset <= CastedBase.block()->getSize());
return Pointer(const_cast<Block *>(Base.block()), Offset, Offset);
}

FunctionPointer MemberPointer::toFunctionPointer(const Context &Ctx) const {
return FunctionPointer(Ctx.getProgram().getFunction(cast<FunctionDecl>(Dcl)));
}

APValue MemberPointer::toAPValue() const {
if (isZero())
return APValue(static_cast<ValueDecl *>(nullptr), /*IsDerivedMember=*/false,
/*Path=*/{});

if (hasBase())
return Base.toAPValue();

return APValue(cast<ValueDecl>(getDecl()), /*IsDerivedMember=*/false,
/*Path=*/{});
}

} // namespace interp
} // namespace clang
112 changes: 112 additions & 0 deletions clang/lib/AST/Interp/MemberPointer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
//===------------------------- MemberPointer.h ------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_AST_INTERP_MEMBER_POINTER_H
#define LLVM_CLANG_AST_INTERP_MEMBER_POINTER_H

#include "Pointer.h"
#include <optional>

namespace clang {
class ASTContext;
namespace interp {

class Context;
class FunctionPointer;

class MemberPointer final {
private:
Pointer Base;
const Decl *Dcl = nullptr;
int32_t PtrOffset = 0;

MemberPointer(Pointer Base, const Decl *Dcl, int32_t PtrOffset)
: Base(Base), Dcl(Dcl), PtrOffset(PtrOffset) {}

public:
MemberPointer() = default;
MemberPointer(Pointer Base, const Decl *Dcl) : Base(Base), Dcl(Dcl) {}
MemberPointer(uint32_t Address, const Descriptor *D) {
// We only reach this for Address == 0, when creating a null member pointer.
assert(Address == 0);
}

MemberPointer(const Decl *D) : Dcl(D) {
assert((isa<FieldDecl, IndirectFieldDecl, CXXMethodDecl>(D)));
}

uint64_t getIntegerRepresentation() const {
assert(
false &&
"getIntegerRepresentation() shouldn't be reachable for MemberPointers");
return 17;
}

std::optional<Pointer> toPointer(const Context &Ctx) const;

FunctionPointer toFunctionPointer(const Context &Ctx) const;

Pointer getBase() const {
if (PtrOffset < 0)
return Base.atField(-PtrOffset);
return Base.atFieldSub(PtrOffset);
}
bool isMemberFunctionPointer() const {
return isa_and_nonnull<CXXMethodDecl>(Dcl);
}
const CXXMethodDecl *getMemberFunction() const {
return dyn_cast_if_present<CXXMethodDecl>(Dcl);
}
const FieldDecl *getField() const {
return dyn_cast_if_present<FieldDecl>(Dcl);
}

bool hasDecl() const { return Dcl; }
const Decl *getDecl() const { return Dcl; }

MemberPointer atInstanceBase(unsigned Offset) const {
if (Base.isZero())
return MemberPointer(Base, Dcl, Offset);
return MemberPointer(this->Base, Dcl, Offset + PtrOffset);
}

MemberPointer takeInstance(Pointer Instance) const {
assert(this->Base.isZero());
return MemberPointer(Instance, this->Dcl, this->PtrOffset);
}

APValue toAPValue() const;

bool isZero() const { return Base.isZero() && !Dcl; }
bool hasBase() const { return !Base.isZero(); }

void print(llvm::raw_ostream &OS) const {
OS << "MemberPtr(" << Base << " " << (void *)Dcl << " + " << PtrOffset
<< ")";
}

std::string toDiagnosticString(const ASTContext &Ctx) const {
return "FIXME";
}

ComparisonCategoryResult compare(const MemberPointer &RHS) const {
if (this->Dcl == RHS.Dcl)
return ComparisonCategoryResult::Equal;
return ComparisonCategoryResult::Unordered;
}
};

inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, MemberPointer FP) {
FP.print(OS);
return OS;
}

} // namespace interp
} // namespace clang

#endif
18 changes: 16 additions & 2 deletions clang/lib/AST/Interp/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def IntAPS : Type;
def Float : Type;
def Ptr : Type;
def FnPtr : Type;
def MemberPtr : Type;

//===----------------------------------------------------------------------===//
// Types transferred to the interpreter.
Expand Down Expand Up @@ -61,6 +62,7 @@ def ArgOffsetOfExpr : ArgType { let Name = "const OffsetOfExpr *"; }
def ArgDeclRef : ArgType { let Name = "const DeclRefExpr *"; }
def ArgDesc : ArgType { let Name = "const Descriptor *"; }
def ArgCCI : ArgType { let Name = "const ComparisonCategoryInfo *"; }
def ArgDecl : ArgType { let Name = "const Decl*"; }

//===----------------------------------------------------------------------===//
// Classes of types instructions operate on.
Expand Down Expand Up @@ -93,7 +95,7 @@ def AluTypeClass : TypeClass {
}

def PtrTypeClass : TypeClass {
let Types = [Ptr, FnPtr];
let Types = [Ptr, FnPtr, MemberPtr];
}

def BoolTypeClass : TypeClass {
Expand Down Expand Up @@ -208,7 +210,6 @@ def CallBI : Opcode {

def CallPtr : Opcode {
let Args = [ArgUint32, ArgCallExpr];
let Types = [];
}

def CallVar : Opcode {
Expand Down Expand Up @@ -327,6 +328,11 @@ def GetPtrBasePop : Opcode {
// Offset of field, which is a base.
let Args = [ArgUint32];
}
def GetMemberPtrBasePop : Opcode {
// Offset of field, which is a base.
let Args = [ArgSint32];
}


def FinishInitPop : Opcode;
def FinishInit : Opcode;
Expand Down Expand Up @@ -751,6 +757,14 @@ def CheckNonNullArg : Opcode {

def Memcpy : Opcode;

def ToMemberPtr : Opcode;
def CastMemberPtrPtr : Opcode;
def GetMemberPtr : Opcode {
let Args = [ArgDecl];
}
def GetMemberPtrBase : Opcode;
def GetMemberPtrDecl : Opcode;

//===----------------------------------------------------------------------===//
// Debugging.
//===----------------------------------------------------------------------===//
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/Pointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "Function.h"
#include "Integral.h"
#include "InterpBlock.h"
#include "MemberPointer.h"
#include "PrimType.h"
#include "Record.h"

Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/Pointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,7 @@ class Pointer {
private:
friend class Block;
friend class DeadBlock;
friend class MemberPointer;
friend struct InitMap;

Pointer(Block *Pointee, unsigned Base, uint64_t Offset);
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/PrimType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "Floating.h"
#include "FunctionPointer.h"
#include "IntegralAP.h"
#include "MemberPointer.h"
#include "Pointer.h"

using namespace clang;
Expand Down
8 changes: 7 additions & 1 deletion clang/lib/AST/Interp/PrimType.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class Pointer;
class Boolean;
class Floating;
class FunctionPointer;
class MemberPointer;
template <bool Signed> class IntegralAP;
template <unsigned Bits, bool Signed> class Integral;

Expand All @@ -44,10 +45,11 @@ enum PrimType : unsigned {
PT_Float = 11,
PT_Ptr = 12,
PT_FnPtr = 13,
PT_MemberPtr = 14,
};

inline constexpr bool isPtrType(PrimType T) {
return T == PT_Ptr || T == PT_FnPtr;
return T == PT_Ptr || T == PT_FnPtr || T == PT_MemberPtr;
}

enum class CastKind : uint8_t {
Expand Down Expand Up @@ -91,6 +93,9 @@ template <> struct PrimConv<PT_Ptr> { using T = Pointer; };
template <> struct PrimConv<PT_FnPtr> {
using T = FunctionPointer;
};
template <> struct PrimConv<PT_MemberPtr> {
using T = MemberPointer;
};

/// Returns the size of a primitive type in bytes.
size_t primSize(PrimType Type);
Expand Down Expand Up @@ -131,6 +136,7 @@ static inline bool aligned(const void *P) {
TYPE_SWITCH_CASE(PT_Bool, B) \
TYPE_SWITCH_CASE(PT_Ptr, B) \
TYPE_SWITCH_CASE(PT_FnPtr, B) \
TYPE_SWITCH_CASE(PT_MemberPtr, B) \
} \
} while (0)

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/OpenACCClause.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ OpenACCClause::child_range OpenACCClause::children() {
#define VISIT_CLAUSE(CLAUSE_NAME) \
case OpenACCClauseKind::CLAUSE_NAME: \
return cast<OpenACC##CLAUSE_NAME##Clause>(this)->children();
#define CLAUSE_ALIAS(ALIAS_NAME, CLAUSE_NAME) \
#define CLAUSE_ALIAS(ALIAS_NAME, CLAUSE_NAME, DEPRECATED) \
case OpenACCClauseKind::ALIAS_NAME: \
return cast<OpenACC##CLAUSE_NAME##Clause>(this)->children();

Expand Down
16 changes: 0 additions & 16 deletions clang/lib/AST/ParentMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,22 +97,6 @@ static void BuildParentMap(MapTy& M, Stmt* S,
BuildParentMap(M, SubStmt, OVMode);
}
break;
case Stmt::CXXDefaultArgExprClass:
if (auto *Arg = dyn_cast<CXXDefaultArgExpr>(S)) {
if (Arg->hasRewrittenInit()) {
M[Arg->getExpr()] = S;
BuildParentMap(M, Arg->getExpr(), OVMode);
}
}
break;
case Stmt::CXXDefaultInitExprClass:
if (auto *Init = dyn_cast<CXXDefaultInitExpr>(S)) {
if (Init->hasRewrittenInit()) {
M[Init->getExpr()] = S;
BuildParentMap(M, Init->getExpr(), OVMode);
}
}
break;
default:
for (Stmt *SubStmt : S->children()) {
if (SubStmt) {
Expand Down
21 changes: 13 additions & 8 deletions clang/lib/AST/TextNodeDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,9 @@ void TextNodeDumper::dumpTemplateArgument(const TemplateArgument &TA) {
}
OS << " '" << Str << "'";

if (!Context)
return;

if (TemplateArgument CanonTA = Context->getCanonicalTemplateArgument(TA);
!CanonTA.structurallyEquals(TA)) {
llvm::SmallString<128> CanonStr;
Expand Down Expand Up @@ -1139,15 +1142,17 @@ void TextNodeDumper::dumpTemplateName(TemplateName TN, StringRef Label) {
}
OS << " '" << Str << "'";

if (TemplateName CanonTN = Context->getCanonicalTemplateName(TN);
CanonTN != TN) {
llvm::SmallString<128> CanonStr;
{
llvm::raw_svector_ostream SS(CanonStr);
CanonTN.print(SS, PrintPolicy);
if (Context) {
if (TemplateName CanonTN = Context->getCanonicalTemplateName(TN);
CanonTN != TN) {
llvm::SmallString<128> CanonStr;
{
llvm::raw_svector_ostream SS(CanonStr);
CanonTN.print(SS, PrintPolicy);
}
if (CanonStr != Str)
OS << ":'" << CanonStr << "'";
}
if (CanonStr != Str)
OS << ":'" << CanonStr << "'";
}
}
dumpBareTemplateName(TN);
Expand Down
38 changes: 37 additions & 1 deletion clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2749,6 +2749,43 @@ bool QualType::isTriviallyCopyableType(const ASTContext &Context) const {
/*IsCopyConstructible=*/false);
}

// FIXME: each call will trigger a full computation, cache the result.
bool QualType::isBitwiseCloneableType(const ASTContext &Context) const {
auto CanonicalType = getCanonicalType();
if (CanonicalType.hasNonTrivialObjCLifetime())
return false;
if (CanonicalType->isArrayType())
return Context.getBaseElementType(CanonicalType)
.isBitwiseCloneableType(Context);

if (CanonicalType->isIncompleteType())
return false;
const auto *RD = CanonicalType->getAsRecordDecl(); // struct/union/class
if (!RD)
return true;

// Never allow memcpy when we're adding poisoned padding bits to the struct.
// Accessing these posioned bits will trigger false alarms on
// SanitizeAddressFieldPadding etc.
if (RD->mayInsertExtraPadding())
return false;

for (auto *const Field : RD->fields()) {
if (!Field->getType().isBitwiseCloneableType(Context))
return false;
}

if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
for (auto Base : CXXRD->bases())
if (!Base.getType().isBitwiseCloneableType(Context))
return false;
for (auto VBase : CXXRD->vbases())
if (!VBase.getType().isBitwiseCloneableType(Context))
return false;
}
return true;
}

bool QualType::isTriviallyCopyConstructibleType(
const ASTContext &Context) const {
return isTriviallyCopyableTypeImpl(*this, Context,
Expand Down Expand Up @@ -4444,7 +4481,6 @@ static CachedProperties computeCachedProperties(const Type *T) {
#define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class,Base) case Type::Class:
#include "clang/AST/TypeNodes.inc"
// Treat instantiation-dependent types as external.
if (!T->isInstantiationDependentType()) T->dump();
assert(T->isInstantiationDependentType());
return CachedProperties(Linkage::External, false);

Expand Down
50 changes: 9 additions & 41 deletions clang/lib/Analysis/CFG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -556,10 +556,6 @@ class CFGBuilder {

private:
// Visitors to walk an AST and construct the CFG.
CFGBlock *VisitCXXDefaultArgExpr(CXXDefaultArgExpr *Default,
AddStmtChoice asc);
CFGBlock *VisitCXXDefaultInitExpr(CXXDefaultInitExpr *Default,
AddStmtChoice asc);
CFGBlock *VisitInitListExpr(InitListExpr *ILE, AddStmtChoice asc);
CFGBlock *VisitAddrLabelExpr(AddrLabelExpr *A, AddStmtChoice asc);
CFGBlock *VisitAttributedStmt(AttributedStmt *A, AddStmtChoice asc);
Expand Down Expand Up @@ -2258,10 +2254,16 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc,
asc, ExternallyDestructed);

case Stmt::CXXDefaultArgExprClass:
return VisitCXXDefaultArgExpr(cast<CXXDefaultArgExpr>(S), asc);

case Stmt::CXXDefaultInitExprClass:
return VisitCXXDefaultInitExpr(cast<CXXDefaultInitExpr>(S), asc);
// FIXME: The expression inside a CXXDefaultArgExpr is owned by the
// called function's declaration, not by the caller. If we simply add
// this expression to the CFG, we could end up with the same Expr
// appearing multiple times (PR13385).
//
// It's likewise possible for multiple CXXDefaultInitExprs for the same
// expression to be used in the same function (through aggregate
// initialization).
return VisitStmt(S, asc);

case Stmt::CXXBindTemporaryExprClass:
return VisitCXXBindTemporaryExpr(cast<CXXBindTemporaryExpr>(S), asc);
Expand Down Expand Up @@ -2431,40 +2433,6 @@ CFGBlock *CFGBuilder::VisitChildren(Stmt *S) {
return B;
}

CFGBlock *CFGBuilder::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *Arg,
AddStmtChoice asc) {
if (Arg->hasRewrittenInit()) {
if (asc.alwaysAdd(*this, Arg)) {
autoCreateBlock();
appendStmt(Block, Arg);
}
return VisitStmt(Arg->getExpr(), asc);
}

// We can't add the default argument if it's not rewritten because the
// expression inside a CXXDefaultArgExpr is owned by the called function's
// declaration, not by the caller, we could end up with the same expression
// appearing multiple times.
return VisitStmt(Arg, asc);
}

CFGBlock *CFGBuilder::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *Init,
AddStmtChoice asc) {
if (Init->hasRewrittenInit()) {
if (asc.alwaysAdd(*this, Init)) {
autoCreateBlock();
appendStmt(Block, Init);
}
return VisitStmt(Init->getExpr(), asc);
}

// We can't add the default initializer if it's not rewritten because multiple
// CXXDefaultInitExprs for the same sub-expression to be used in the same
// function (through aggregate initialization). we could end up with the same
// expression appearing multiple times.
return VisitStmt(Init, asc);
}

CFGBlock *CFGBuilder::VisitInitListExpr(InitListExpr *ILE, AddStmtChoice asc) {
if (asc.alwaysAdd(*this, ILE)) {
autoCreateBlock();
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Basic/Cuda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ static const CudaArchToStringMap arch_names[] = {
GFX(1103), // gfx1103
GFX(1150), // gfx1150
GFX(1151), // gfx1151
GFX(1152), // gfx1152
{CudaArch::GFX12_GENERIC, "gfx12-generic", "compute_amdgcn"},
GFX(1200), // gfx1200
GFX(1201), // gfx1201
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Basic/Targets/LoongArch.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ class LLVM_LIBRARY_VISIBILITY LoongArch64TargetInfo
LongWidth = LongAlign = PointerWidth = PointerAlign = 64;
IntMaxType = Int64Type = SignedLong;
HasUnalignedAccess = true;
resetDataLayout("e-m:e-p:64:64-i64:64-i128:128-n64-S128");
resetDataLayout("e-m:e-p:64:64-i64:64-i128:128-n32:64-S128");
// TODO: select appropriate ABI.
setABI("lp64d");
}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Basic/Targets/NVPTX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ void NVPTXTargetInfo::getTargetDefines(const LangOptions &Opts,
case CudaArch::GFX1103:
case CudaArch::GFX1150:
case CudaArch::GFX1151:
case CudaArch::GFX1152:
case CudaArch::GFX12_GENERIC:
case CudaArch::GFX1200:
case CudaArch::GFX1201:
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3537,6 +3537,7 @@ void CGOpenMPRuntimeGPU::processRequiresDirective(
case CudaArch::GFX1103:
case CudaArch::GFX1150:
case CudaArch::GFX1151:
case CudaArch::GFX1152:
case CudaArch::GFX12_GENERIC:
case CudaArch::GFX1200:
case CudaArch::GFX1201:
Expand Down
6 changes: 5 additions & 1 deletion clang/lib/CodeGen/Targets/AMDGPU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,11 @@ void AMDGPUABIInfo::computeInfo(CGFunctionInfo &FI) const {

Address AMDGPUABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
QualType Ty) const {
llvm_unreachable("AMDGPU does not support varargs");
const bool IsIndirect = false;
const bool AllowHigherAlign = false;
return emitVoidPtrVAArg(CGF, VAListAddr, Ty, IsIndirect,
getContext().getTypeInfoInChars(Ty),
CharUnits::fromQuantity(4), AllowHigherAlign);
}

ABIArgInfo AMDGPUABIInfo::classifyReturnType(QualType RetTy) const {
Expand Down
5 changes: 2 additions & 3 deletions clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
using namespace clang;
using namespace clang::extractapi;
using namespace llvm;
using namespace llvm::json;

namespace {

Expand Down Expand Up @@ -1036,9 +1035,9 @@ void SymbolGraphSerializer::serializeGraphToStream(
ExtendedModule &&EM) {
Object Root = serializeGraph(ModuleName, std::move(EM));
if (Options.Compact)
OS << formatv("{0}", Value(std::move(Root))) << "\n";
OS << formatv("{0}", json::Value(std::move(Root))) << "\n";
else
OS << formatv("{0:2}", Value(std::move(Root))) << "\n";
OS << formatv("{0:2}", json::Value(std::move(Root))) << "\n";
}

void SymbolGraphSerializer::serializeMainSymbolGraph(
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/Format/UnwrappedLineParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1181,10 +1181,10 @@ void UnwrappedLineParser::parsePPDefine() {
Line->InMacroBody = true;

if (Style.SkipMacroDefinitionBody) {
do {
while (!eof()) {
FormatTok->Finalized = true;
nextToken();
} while (!eof());
FormatTok = Tokens->getNextToken();
}
addUnwrappedLine();
return;
}
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Frontend/FrontendActions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1169,8 +1169,8 @@ void PrintDependencyDirectivesSourceMinimizerAction::ExecuteAction() {
llvm::SmallVector<dependency_directives_scan::Token, 16> Tokens;
llvm::SmallVector<dependency_directives_scan::Directive, 32> Directives;
if (scanSourceForDependencyDirectives(
FromFile.getBuffer(), Tokens, Directives, CI.getLangOpts(),
&CI.getDiagnostics(), SM.getLocForStartOfFile(SM.getMainFileID()))) {
FromFile.getBuffer(), Tokens, Directives, &CI.getDiagnostics(),
SM.getLocForStartOfFile(SM.getMainFileID()))) {
assert(CI.getDiagnostics().hasErrorOccurred() &&
"no errors reported for failure");

Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Interpreter/IncrementalParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,8 @@ void IncrementalParser::CleanUpPTU(PartialTranslationUnit &PTU) {
if (!ND)
continue;
// Check if we need to clean up the IdResolver chain.
if (ND->getDeclName().getFETokenInfo())
if (ND->getDeclName().getFETokenInfo() && !D->getLangOpts().ObjC &&
!D->getLangOpts().CPlusPlus)
getCI()->getSema().IdResolver.RemoveDecl(ND);
}
}
Expand Down
165 changes: 91 additions & 74 deletions clang/lib/Interpreter/Interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TargetParser/Host.h"

#include <cstdarg>

using namespace clang;

// FIXME: Figure out how to unify with namespace init_convenience from
Expand Down Expand Up @@ -270,14 +273,10 @@ Interpreter::~Interpreter() {
// can't find the precise resource directory in unittests so we have to hard
// code them.
const char *const Runtimes = R"(
#define __CLANG_REPL__ 1
#ifdef __cplusplus
#define EXTERN_C extern "C"
void *__clang_Interpreter_SetValueWithAlloc(void*, void*, void*);
void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*);
void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, void*);
void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, float);
void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, double);
void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, long double);
void __clang_Interpreter_SetValueNoAlloc(void*,void*,void*,unsigned long long);
struct __clang_Interpreter_NewTag{} __ci_newtag;
void* operator new(__SIZE_TYPE__, void* __p, __clang_Interpreter_NewTag) noexcept;
template <class T, class = T (*)() /*disable for arrays*/>
Expand All @@ -289,7 +288,11 @@ const char *const Runtimes = R"(
void __clang_Interpreter_SetValueCopyArr(const T (*Src)[N], void* Placement, unsigned long Size) {
__clang_Interpreter_SetValueCopyArr(Src[0], Placement, Size);
}
#else
#define EXTERN_C extern
#endif // __cplusplus

EXTERN_C void __clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType, ...);
)";

llvm::Expected<std::unique_ptr<Interpreter>>
Expand Down Expand Up @@ -588,15 +591,17 @@ std::unique_ptr<RuntimeInterfaceBuilder> Interpreter::FindRuntimeInterface() {
if (!LookupInterface(ValuePrintingInfo[NoAlloc],
MagicRuntimeInterface[NoAlloc]))
return nullptr;
if (!LookupInterface(ValuePrintingInfo[WithAlloc],
MagicRuntimeInterface[WithAlloc]))
return nullptr;
if (!LookupInterface(ValuePrintingInfo[CopyArray],
MagicRuntimeInterface[CopyArray]))
return nullptr;
if (!LookupInterface(ValuePrintingInfo[NewTag],
MagicRuntimeInterface[NewTag]))
return nullptr;
if (Ctx.getLangOpts().CPlusPlus) {
if (!LookupInterface(ValuePrintingInfo[WithAlloc],
MagicRuntimeInterface[WithAlloc]))
return nullptr;
if (!LookupInterface(ValuePrintingInfo[CopyArray],
MagicRuntimeInterface[CopyArray]))
return nullptr;
if (!LookupInterface(ValuePrintingInfo[NewTag],
MagicRuntimeInterface[NewTag]))
return nullptr;
}

return createInProcessRuntimeInterfaceBuilder(*this, Ctx, S);
}
Expand Down Expand Up @@ -855,69 +860,81 @@ __clang_Interpreter_SetValueWithAlloc(void *This, void *OutVal,
return VRef.getPtr();
}

// Pointers, lvalue struct that can take as a reference.
REPL_EXTERNAL_VISIBILITY void
__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType,
void *Val) {
extern "C" void REPL_EXTERNAL_VISIBILITY __clang_Interpreter_SetValueNoAlloc(
void *This, void *OutVal, void *OpaqueType, ...) {
Value &VRef = *(Value *)OutVal;
VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
VRef.setPtr(Val);
}
Interpreter *I = static_cast<Interpreter *>(This);
VRef = Value(I, OpaqueType);
if (VRef.isVoid())
return;

REPL_EXTERNAL_VISIBILITY void
__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal,
void *OpaqueType) {
Value &VRef = *(Value *)OutVal;
VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
}
va_list args;
va_start(args, /*last named param*/ OpaqueType);

static void SetValueDataBasedOnQualType(Value &V, unsigned long long Data) {
QualType QT = V.getType();
if (const auto *ET = QT->getAs<EnumType>())
QT = ET->getDecl()->getIntegerType();

switch (QT->castAs<BuiltinType>()->getKind()) {
default:
llvm_unreachable("unknown type kind!");
#define X(type, name) \
case BuiltinType::name: \
V.set##name(Data); \
break;
REPL_BUILTIN_TYPES
#undef X
QualType QT = VRef.getType();
if (VRef.getKind() == Value::K_PtrOrObj) {
VRef.setPtr(va_arg(args, void *));
} else {
if (const auto *ET = QT->getAs<EnumType>())
QT = ET->getDecl()->getIntegerType();
switch (QT->castAs<BuiltinType>()->getKind()) {
default:
llvm_unreachable("unknown type kind!");
break;
// Types shorter than int are resolved as int, else va_arg has UB.
case BuiltinType::Bool:
VRef.setBool(va_arg(args, int));
break;
case BuiltinType::Char_S:
VRef.setChar_S(va_arg(args, int));
break;
case BuiltinType::SChar:
VRef.setSChar(va_arg(args, int));
break;
case BuiltinType::Char_U:
VRef.setChar_U(va_arg(args, unsigned));
break;
case BuiltinType::UChar:
VRef.setUChar(va_arg(args, unsigned));
break;
case BuiltinType::Short:
VRef.setShort(va_arg(args, int));
break;
case BuiltinType::UShort:
VRef.setUShort(va_arg(args, unsigned));
break;
case BuiltinType::Int:
VRef.setInt(va_arg(args, int));
break;
case BuiltinType::UInt:
VRef.setUInt(va_arg(args, unsigned));
break;
case BuiltinType::Long:
VRef.setLong(va_arg(args, long));
break;
case BuiltinType::ULong:
VRef.setULong(va_arg(args, unsigned long));
break;
case BuiltinType::LongLong:
VRef.setLongLong(va_arg(args, long long));
break;
case BuiltinType::ULongLong:
VRef.setULongLong(va_arg(args, unsigned long long));
break;
// Types shorter than double are resolved as double, else va_arg has UB.
case BuiltinType::Float:
VRef.setFloat(va_arg(args, double));
break;
case BuiltinType::Double:
VRef.setDouble(va_arg(args, double));
break;
case BuiltinType::LongDouble:
VRef.setLongDouble(va_arg(args, long double));
break;
// See REPL_BUILTIN_TYPES.
}
}
}

REPL_EXTERNAL_VISIBILITY void
__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType,
unsigned long long Val) {
Value &VRef = *(Value *)OutVal;
VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
SetValueDataBasedOnQualType(VRef, Val);
}

REPL_EXTERNAL_VISIBILITY void
__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType,
float Val) {
Value &VRef = *(Value *)OutVal;
VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
VRef.setFloat(Val);
}

REPL_EXTERNAL_VISIBILITY void
__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType,
double Val) {
Value &VRef = *(Value *)OutVal;
VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
VRef.setDouble(Val);
}

REPL_EXTERNAL_VISIBILITY void
__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType,
long double Val) {
Value &VRef = *(Value *)OutVal;
VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
VRef.setLongDouble(Val);
va_end(args);
}

// A trampoline to work around the fact that operator placement new cannot
Expand Down
22 changes: 9 additions & 13 deletions clang/lib/Lex/DependencyDirectivesScanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,14 @@ struct DirectiveWithTokens {
struct Scanner {
Scanner(StringRef Input,
SmallVectorImpl<dependency_directives_scan::Token> &Tokens,
DiagnosticsEngine *Diags, SourceLocation InputSourceLoc,
const LangOptions &LangOpts)
DiagnosticsEngine *Diags, SourceLocation InputSourceLoc)
: Input(Input), Tokens(Tokens), Diags(Diags),
InputSourceLoc(InputSourceLoc),
LangOpts(getLangOptsForDepScanning(LangOpts)),
TheLexer(InputSourceLoc, this->LangOpts, Input.begin(), Input.begin(),
InputSourceLoc(InputSourceLoc), LangOpts(getLangOptsForDepScanning()),
TheLexer(InputSourceLoc, LangOpts, Input.begin(), Input.begin(),
Input.end()) {}

static LangOptions
getLangOptsForDepScanning(const LangOptions &invocationLangOpts) {
LangOptions LangOpts(invocationLangOpts);
static LangOptions getLangOptsForDepScanning() {
LangOptions LangOpts;
// Set the lexer to use 'tok::at' for '@', instead of 'tok::unknown'.
LangOpts.ObjC = true;
LangOpts.LineComment = true;
Expand Down Expand Up @@ -703,7 +700,7 @@ bool Scanner::lex_Pragma(const char *&First, const char *const End) {
SmallVector<dependency_directives_scan::Token> DiscardTokens;
const char *Begin = Buffer.c_str();
Scanner PragmaScanner{StringRef(Begin, Buffer.size()), DiscardTokens, Diags,
InputSourceLoc, LangOptions()};
InputSourceLoc};

PragmaScanner.TheLexer.setParsingPreprocessorDirective(true);
if (PragmaScanner.lexPragma(Begin, Buffer.end()))
Expand Down Expand Up @@ -953,10 +950,9 @@ bool Scanner::scan(SmallVectorImpl<Directive> &Directives) {

bool clang::scanSourceForDependencyDirectives(
StringRef Input, SmallVectorImpl<dependency_directives_scan::Token> &Tokens,
SmallVectorImpl<Directive> &Directives, const LangOptions &LangOpts,
DiagnosticsEngine *Diags, SourceLocation InputSourceLoc) {
return Scanner(Input, Tokens, Diags, InputSourceLoc, LangOpts)
.scan(Directives);
SmallVectorImpl<Directive> &Directives, DiagnosticsEngine *Diags,
SourceLocation InputSourceLoc) {
return Scanner(Input, Tokens, Diags, InputSourceLoc).scan(Directives);
}

void clang::printDependencyDirectivesAsSource(
Expand Down
5 changes: 1 addition & 4 deletions clang/lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -571,11 +571,8 @@ StmtResult Parser::ParseExprStatement(ParsedStmtContext StmtCtx) {
}

Token *CurTok = nullptr;
// If the semicolon is missing at the end of REPL input, consider if
// we want to do value printing. Note this is only enabled in C++ mode
// since part of the implementation requires C++ language features.
// Note we shouldn't eat the token since the callback needs it.
if (Tok.is(tok::annot_repl_input_end) && Actions.getLangOpts().CPlusPlus)
if (Tok.is(tok::annot_repl_input_end))
CurTok = &Tok;
else
// Otherwise, eat the semicolon.
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Sema/Scope.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,11 @@ void Scope::dumpImpl(raw_ostream &OS) const {
{CompoundStmtScope, "CompoundStmtScope"},
{ClassInheritanceScope, "ClassInheritanceScope"},
{CatchScope, "CatchScope"},
{ConditionVarScope, "ConditionVarScope"},
{OpenMPOrderClauseScope, "OpenMPOrderClauseScope"},
{LambdaScope, "LambdaScope"},
{OpenACCComputeConstructScope, "OpenACCComputeConstructScope"},
{TypeAliasScope, "TypeAliasScope"},
{FriendScope, "FriendScope"},
};

Expand Down
6 changes: 3 additions & 3 deletions clang/lib/Sema/SemaAMDGPU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ bool SemaAMDGPU::CheckAMDGCNBuiltinFunctionCall(unsigned BuiltinID,
constexpr const int SizeIdx = 2;
llvm::APSInt Size;
Expr *ArgExpr = TheCall->getArg(SizeIdx);
ExprResult R = SemaRef.VerifyIntegerConstantExpression(ArgExpr, &Size);
if (R.isInvalid())
return true;
[[maybe_unused]] ExprResult R =
SemaRef.VerifyIntegerConstantExpression(ArgExpr, &Size);
assert(!R.isInvalid());
switch (Size.getSExtValue()) {
case 1:
case 2:
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2288,7 +2288,8 @@ void Sema::ActOnPopScope(SourceLocation Loc, Scope *S) {
// Partial translation units that are created in incremental processing must
// not clean up the IdResolver because PTUs should take into account the
// declarations that came from previous PTUs.
if (!PP.isIncrementalProcessingEnabled() || getLangOpts().ObjC)
if (!PP.isIncrementalProcessingEnabled() || getLangOpts().ObjC ||
getLangOpts().CPlusPlus)
IdResolver.RemoveDecl(D);

// Warn on it if we are shadowing a declaration.
Expand Down
35 changes: 9 additions & 26 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5572,9 +5572,10 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
Res = Immediate.TransformInitializer(Param->getInit(),
/*NotCopy=*/false);
});
if (Res.isUsable())
Res = ConvertParamDefaultArgument(Param, Res.get(),
Res.get()->getBeginLoc());
if (Res.isInvalid())
return ExprError();
Res = ConvertParamDefaultArgument(Param, Res.get(),
Res.get()->getBeginLoc());
if (Res.isInvalid())
return ExprError();
Init = Res.get();
Expand Down Expand Up @@ -5608,10 +5609,9 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
InitializationContext.emplace(Loc, Field, CurContext);

Expr *Init = nullptr;
bool HasRewrittenInit = false;

bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
bool InLifetimeExtendingContext = isInLifetimeExtendingContext();

EnterExpressionEvaluationContext EvalContext(
*this, ExpressionEvaluationContext::PotentiallyEvaluated, Field);

Expand Down Expand Up @@ -5646,36 +5646,19 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
ImmediateCallVisitor V(getASTContext());
if (!NestedDefaultChecking)
V.TraverseDecl(Field);

// CWG1815
// Support lifetime extension of temporary created by aggregate
// initialization using a default member initializer. We should always rebuild
// the initializer if it contains any temporaries (if the initializer
// expression is an ExprWithCleanups). Then make sure the normal lifetime
// extension code recurses into the default initializer and does lifetime
// extension when warranted.
bool ContainsAnyTemporaries =
isa_and_present<ExprWithCleanups>(Field->getInClassInitializer());
if (V.HasImmediateCalls || InLifetimeExtendingContext ||
ContainsAnyTemporaries) {
HasRewrittenInit = true;
if (V.HasImmediateCalls) {
ExprEvalContexts.back().DelayedDefaultInitializationContext = {Loc, Field,
CurContext};
ExprEvalContexts.back().IsCurrentlyCheckingDefaultArgumentOrInitializer =
NestedDefaultChecking;
// Pass down lifetime extending flag, and collect temporaries in
// CreateMaterializeTemporaryExpr when we rewrite the call argument.
keepInLifetimeExtendingContext();

EnsureImmediateInvocationInDefaultArgs Immediate(*this);
ExprResult Res;

// Rebuild CXXDefaultInitExpr might cause diagnostics.
SFINAETrap Trap(*this);
runWithSufficientStackSpace(Loc, [&] {
Res = Immediate.TransformInitializer(Field->getInClassInitializer(),
/*CXXDirectInit=*/false);
});
if (Res.isUsable())
if (!Res.isInvalid())
Res = ConvertMemberDefaultInitExpression(Field, Res.get(), Loc);
if (Res.isInvalid()) {
Field->setInvalidDecl();
Expand All @@ -5702,7 +5685,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {

return CXXDefaultInitExpr::Create(Context, InitializationContext->Loc,
Field, InitializationContext->Context,
HasRewrittenInit ? Init : nullptr);
Init);
}

// DR1351:
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1555,6 +1555,9 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo,
bool ListInitialization) {
QualType Ty = TInfo->getType();
SourceLocation TyBeginLoc = TInfo->getTypeLoc().getBeginLoc();

assert((!ListInitialization || Exprs.size() == 1) &&
"List initialization must have exactly one expression.");
SourceRange FullRange = SourceRange(TyBeginLoc, RParenOrBraceLoc);

InitializedEntity Entity =
Expand Down Expand Up @@ -5126,6 +5129,7 @@ static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, TypeTrait UTT,
case UTT_IsStandardLayout:
case UTT_IsPOD:
case UTT_IsLiteral:
case UTT_IsBitwiseCloneable:
// By analogy, is_trivially_relocatable and is_trivially_equality_comparable
// impose the same constraints.
case UTT_IsTriviallyRelocatable:
Expand Down Expand Up @@ -5619,6 +5623,8 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
return C.hasUniqueObjectRepresentations(T);
case UTT_IsTriviallyRelocatable:
return T.isTriviallyRelocatableType(C);
case UTT_IsBitwiseCloneable:
return T.isBitwiseCloneableType(C);
case UTT_IsReferenceable:
return T.isReferenceable();
case UTT_CanPassInRegs:
Expand Down
19 changes: 18 additions & 1 deletion clang/lib/Sema/SemaInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8063,6 +8063,11 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
enum PathLifetimeKind {
/// Lifetime-extend along this path.
Extend,
/// We should lifetime-extend, but we don't because (due to technical
/// limitations) we can't. This happens for default member initializers,
/// which we don't clone for every use, so we don't have a unique
/// MaterializeTemporaryExpr to update.
ShouldExtend,
/// Do not lifetime extend along this path.
NoExtend
};
Expand All @@ -8074,7 +8079,7 @@ shouldLifetimeExtendThroughPath(const IndirectLocalPath &Path) {
PathLifetimeKind Kind = PathLifetimeKind::Extend;
for (auto Elem : Path) {
if (Elem.Kind == IndirectLocalPathEntry::DefaultInit)
Kind = PathLifetimeKind::Extend;
Kind = PathLifetimeKind::ShouldExtend;
else if (Elem.Kind != IndirectLocalPathEntry::LambdaCaptureInit)
return PathLifetimeKind::NoExtend;
}
Expand Down Expand Up @@ -8194,6 +8199,18 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
ExtendingEntity->allocateManglingNumber());
// Also visit the temporaries lifetime-extended by this initializer.
return true;

case PathLifetimeKind::ShouldExtend:
// We're supposed to lifetime-extend the temporary along this path (per
// the resolution of DR1815), but we don't support that yet.
//
// FIXME: Properly handle this situation. Perhaps the easiest approach
// would be to clone the initializer expression on each use that would
// lifetime extend its temporaries.
Diag(DiagLoc, diag::warn_unsupported_lifetime_extension)
<< RK << DiagRange;
break;

case PathLifetimeKind::NoExtend:
// If the path goes through the initialization of a variable or field,
// it can't possibly reach a temporary created in this full-expression.
Expand Down
1,191 changes: 656 additions & 535 deletions clang/lib/Sema/SemaOpenACC.cpp

Large diffs are not rendered by default.

12 changes: 4 additions & 8 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -14172,13 +14172,6 @@ TreeTransform<Derived>::TransformCXXTemporaryObjectExpr(
if (TransformExprs(E->getArgs(), E->getNumArgs(), true, Args,
&ArgumentChanged))
return ExprError();

if (E->isListInitialization() && !E->isStdInitListInitialization()) {
ExprResult Res = RebuildInitList(E->getBeginLoc(), Args, E->getEndLoc());
if (Res.isInvalid())
return ExprError();
Args = {Res.get()};
}
}

if (!getDerived().AlwaysRebuild() &&
Expand All @@ -14190,9 +14183,12 @@ TreeTransform<Derived>::TransformCXXTemporaryObjectExpr(
return SemaRef.MaybeBindToTemporary(E);
}

// FIXME: We should just pass E->isListInitialization(), but we're not
// prepared to handle list-initialization without a child InitListExpr.
SourceLocation LParenLoc = T->getTypeLoc().getEndLoc();
return getDerived().RebuildCXXTemporaryObjectExpr(
T, LParenLoc, Args, E->getEndLoc(), E->isListInitialization());
T, LParenLoc, Args, E->getEndLoc(),
/*ListInitialization=*/LParenLoc.isInvalid());
}

template<typename Derived>
Expand Down
56 changes: 22 additions & 34 deletions clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1971,45 +1971,33 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
ExplodedNodeSet Tmp;
StmtNodeBuilder Bldr2(PreVisit, Tmp, *currBldrCtx);

bool HasRewrittenInit = false;
const Expr *ArgE = nullptr;
if (const auto *DefE = dyn_cast<CXXDefaultArgExpr>(S)) {
const Expr *ArgE;
if (const auto *DefE = dyn_cast<CXXDefaultArgExpr>(S))
ArgE = DefE->getExpr();
HasRewrittenInit = DefE->hasRewrittenInit();
} else if (const auto *DefE = dyn_cast<CXXDefaultInitExpr>(S)) {
else if (const auto *DefE = dyn_cast<CXXDefaultInitExpr>(S))
ArgE = DefE->getExpr();
HasRewrittenInit = DefE->hasRewrittenInit();
} else
else
llvm_unreachable("unknown constant wrapper kind");

if (HasRewrittenInit) {
for (auto *N : PreVisit) {
ProgramStateRef state = N->getState();
const LocationContext *LCtx = N->getLocationContext();
state = state->BindExpr(S, LCtx, state->getSVal(ArgE, LCtx));
Bldr2.generateNode(S, N, state);
}
} else {
// If it's not rewritten, the contents of these expressions are not
// actually part of the current function, so we fall back to constant
// evaluation.
bool IsTemporary = false;
if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(ArgE)) {
ArgE = MTE->getSubExpr();
IsTemporary = true;
}

std::optional<SVal> ConstantVal = svalBuilder.getConstantVal(ArgE);
const LocationContext *LCtx = Pred->getLocationContext();
for (auto *I : PreVisit) {
ProgramStateRef State = I->getState();
State = State->BindExpr(S, LCtx, ConstantVal.value_or(UnknownVal()));
if (IsTemporary)
State = createTemporaryRegionIfNeeded(State, LCtx, cast<Expr>(S),
cast<Expr>(S));
bool IsTemporary = false;
if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(ArgE)) {
ArgE = MTE->getSubExpr();
IsTemporary = true;
}

Bldr2.generateNode(S, I, State);
}
std::optional<SVal> ConstantVal = svalBuilder.getConstantVal(ArgE);
if (!ConstantVal)
ConstantVal = UnknownVal();

const LocationContext *LCtx = Pred->getLocationContext();
for (const auto I : PreVisit) {
ProgramStateRef State = I->getState();
State = State->BindExpr(S, LCtx, *ConstantVal);
if (IsTemporary)
State = createTemporaryRegionIfNeeded(State, LCtx,
cast<Expr>(S),
cast<Expr>(S));
Bldr2.generateNode(S, I, State);
}

getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ DependencyScanningWorkerFilesystem::readFile(StringRef Filename) {
}

bool DependencyScanningWorkerFilesystem::ensureDirectiveTokensArePopulated(
EntryRef Ref, const LangOptions &LangOpts) {
EntryRef Ref) {
auto &Entry = Ref.Entry;

if (Entry.isError() || Entry.isDirectory())
Expand All @@ -66,7 +66,7 @@ bool DependencyScanningWorkerFilesystem::ensureDirectiveTokensArePopulated(
// dependencies.
if (scanSourceForDependencyDirectives(Contents->Original->getBuffer(),
Contents->DepDirectiveTokens,
Directives, LangOpts)) {
Directives)) {
Contents->DepDirectiveTokens.clear();
// FIXME: Propagate the diagnostic if desired by the client.
Contents->DepDirectives.store(new std::optional<DependencyDirectivesTy>());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -364,12 +364,11 @@ class DependencyScanningAction : public tooling::ToolAction {
// Use the dependency scanning optimized file system if requested to do so.
if (DepFS)
ScanInstance.getPreprocessorOpts().DependencyDirectivesForFile =
[LocalDepFS = DepFS,
&LangOpts = ScanInstance.getLangOpts()](FileEntryRef File)
[LocalDepFS = DepFS](FileEntryRef File)
-> std::optional<ArrayRef<dependency_directives_scan::Directive>> {
if (llvm::ErrorOr<EntryRef> Entry =
LocalDepFS->getOrCreateFileSystemEntry(File.getName()))
if (LocalDepFS->ensureDirectiveTokensArePopulated(*Entry, LangOpts))
if (LocalDepFS->ensureDirectiveTokensArePopulated(*Entry))
return Entry->getDirectiveTokens();
return std::nullopt;
};
Expand Down
14 changes: 14 additions & 0 deletions clang/test/AST/Interp/arrays.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -609,3 +609,17 @@ namespace ArrayMemberAccess {
bool cond = a->x;
}
}

namespace OnePastEndSub {
struct A {};
constexpr A a[3][3];
constexpr int diff2 = &a[1][3] - &a[1][0]; /// Used to crash.
}

static int same_entity_2[3];
constexpr int *get2() {
// This is a redeclaration of the same entity, even though it doesn't
// inherit the type of the prior declaration.
extern int same_entity_2[];
return same_entity_2;
}
22 changes: 22 additions & 0 deletions clang/test/AST/Interp/cxx23.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,25 @@ namespace ExplicitLambdaThis {
};
static_assert(f());
}

namespace std {
struct strong_ordering {
int n;
constexpr operator int() const { return n; }
static const strong_ordering less, equal, greater;
};
constexpr strong_ordering strong_ordering::less = {-1};
constexpr strong_ordering strong_ordering::equal = {0};
constexpr strong_ordering strong_ordering::greater = {1};
}

namespace UndefinedThreeWay {
struct A {
friend constexpr std::strong_ordering operator<=>(const A&, const A&) = default; // all-note {{declared here}}
};

constexpr std::strong_ordering operator<=>(const A&, const A&) noexcept;
constexpr std::strong_ordering (*test_a_threeway)(const A&, const A&) = &operator<=>;
static_assert(!(*test_a_threeway)(A(), A())); // all-error {{static assertion expression is not an integral constant expression}} \
// all-note {{undefined function 'operator<=>' cannot be used in a constant expression}}
}
4 changes: 2 additions & 2 deletions clang/test/AST/Interp/eval-order.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ namespace EvalOrder {
// Rules 1 and 2 have no effect ('b' is not an expression).

// Rule 3: a->*b
// SEQ(A(ud).*B(&UserDefined::n)); FIXME
// SEQ(A(&ud)->*B(&UserDefined::n)); FIXME
SEQ(A(ud).*B(&UserDefined::n));
SEQ(A(&ud)->*B(&UserDefined::n));

// Rule 4: a(b1, b2, b3)
SEQ(A(f)(B(1), B(2), B(3))); // expected-error {{not an integral constant expression}} FIXME \
Expand Down
9 changes: 7 additions & 2 deletions clang/test/AST/Interp/literals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,12 @@ namespace ScalarTypes {
First = 0,
};
static_assert(getScalar<E>() == First, "");
/// FIXME: Member pointers.

struct S {
int v;
};
constexpr int S::* MemberPtr = &S::v;
static_assert(getScalar<decltype(MemberPtr)>() == nullptr, "");

#if __cplusplus >= 201402L
constexpr void Void(int n) {
Expand Down Expand Up @@ -1204,7 +1209,7 @@ namespace incdecbool {
constexpr int externvar1() { // both-error {{never produces a constant expression}}
extern char arr[]; // ref-note {{declared here}}
return arr[0]; // ref-note {{read of non-constexpr variable 'arr'}} \
// expected-note {{array-to-pointer decay of array member without known bound is not supported}}
// expected-note {{indexing of array without known bound}}
}
#endif

Expand Down
197 changes: 197 additions & 0 deletions clang/test/AST/Interp/memberpointers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
// RUN: %clang_cc1 -std=c++14 -fexperimental-new-constant-interpreter -verify=expected,both %s
// RUN: %clang_cc1 -std=c++14 -verify=ref,both %s

namespace MemberPointers {
struct A {
constexpr A(int n) : n(n) {}
int n;
constexpr int f() const { return n + 3; }
};

constexpr A a(7);
static_assert(A(5).*&A::n == 5, "");
static_assert((&a)->*&A::n == 7, "");
static_assert((A(8).*&A::f)() == 11, "");
static_assert(((&a)->*&A::f)() == 10, "");

struct B : A {
constexpr B(int n, int m) : A(n), m(m) {}
int m;
constexpr int g() const { return n + m + 1; }
};
constexpr B b(9, 13);
static_assert(B(4, 11).*&A::n == 4, "");
static_assert(B(4, 11).*&B::m == 11, "");
static_assert(B(4, 11).m == 11, "");
static_assert(B(4, 11).*(int(A::*))&B::m == 11, "");
static_assert(B(4, 11).*&B::m == 11, "");
static_assert((&b)->*&A::n == 9, "");
static_assert((&b)->*&B::m == 13, "");
static_assert((&b)->*(int(A::*))&B::m == 13, "");
static_assert((B(4, 11).*&A::f)() == 7, "");
static_assert((B(4, 11).*&B::g)() == 16, "");

static_assert((B(4, 11).*(int(A::*)() const)&B::g)() == 16, "");

static_assert(((&b)->*&A::f)() == 12, "");
static_assert(((&b)->*&B::g)() == 23, "");
static_assert(((&b)->*(int(A::*)()const)&B::g)() == 23, "");


struct S {
constexpr S(int m, int n, int (S::*pf)() const, int S::*pn) :
m(m), n(n), pf(pf), pn(pn) {}
constexpr S() : m(), n(), pf(&S::f), pn(&S::n) {}

constexpr int f() const { return this->*pn; }
virtual int g() const;

int m, n;
int (S::*pf)() const;
int S::*pn;
};

constexpr int S::*pm = &S::m;
constexpr int S::*pn = &S::n;

constexpr int (S::*pf)() const = &S::f;
constexpr int (S::*pg)() const = &S::g;

constexpr S s(2, 5, &S::f, &S::m);

static_assert((s.*&S::f)() == 2, "");
static_assert((s.*s.pf)() == 2, "");

static_assert(pf == &S::f, "");

static_assert(pf == s.*&S::pf, "");

static_assert(pm == &S::m, "");
static_assert(pm != pn, "");
static_assert(s.pn != pn, "");
static_assert(s.pn == pm, "");
static_assert(pg != nullptr, "");
static_assert(pf != nullptr, "");
static_assert((int S::*)nullptr == nullptr, "");
static_assert(pg == pg, ""); // both-error {{constant expression}} \
// both-note {{comparison of pointer to virtual member function 'g' has unspecified value}}
static_assert(pf != pg, ""); // both-error {{constant expression}} \
// both-note {{comparison of pointer to virtual member function 'g' has unspecified value}}

template<int n> struct T : T<n-1> { const int X = n;};
template<> struct T<0> { int n; char k;};
template<> struct T<30> : T<29> { int m; };

T<17> t17;
T<30> t30;

constexpr int (T<15>::*deepm) = (int(T<10>::*))&T<30>::m;
constexpr int (T<10>::*deepn) = &T<0>::n;
constexpr char (T<10>::*deepk) = &T<0>::k;

static_assert(&(t17.*deepn) == &t17.n, "");
static_assert(&(t17.*deepk) == &t17.k, "");
static_assert(deepn == &T<2>::n, "");

constexpr int *pgood = &(t30.*deepm);
constexpr int *pbad = &(t17.*deepm); // both-error {{constant expression}}
static_assert(&(t30.*deepm) == &t30.m, "");

static_assert(deepm == &T<50>::m, "");
static_assert(deepm != deepn, "");

constexpr T<5> *p17_5 = &t17;
constexpr T<13> *p17_13 = (T<13>*)p17_5;
constexpr T<23> *p17_23 = (T<23>*)p17_13; // both-error {{constant expression}} \
// both-note {{cannot cast object of dynamic type 'T<17>' to type 'T<23>'}}
constexpr T<18> *p17_18 = (T<18>*)p17_13; // both-error {{constant expression}} \
// both-note {{cannot cast object of dynamic type 'T<17>' to type 'T<18>'}}
static_assert(&(p17_5->*(int(T<0>::*))deepn) == &t17.n, "");
static_assert(&(p17_5->*(int(T<0>::*))deepn), "");


static_assert(&(p17_13->*deepn) == &t17.n, "");
constexpr int *pbad2 = &(p17_13->*(int(T<9>::*))deepm); // both-error {{constant expression}}

constexpr T<5> *p30_5 = &t30;
constexpr T<23> *p30_23 = (T<23>*)p30_5;
constexpr T<13> *p30_13 = p30_23;
static_assert(&(p30_13->*deepn) == &t30.n, "");
static_assert(&(p30_23->*deepn) == &t30.n, "");
static_assert(&(p30_5->*(int(T<3>::*))deepn) == &t30.n, "");

static_assert(&(p30_5->*(int(T<2>::*))deepm) == &t30.m, "");
static_assert(&(((T<17>*)p30_13)->*deepm) == &t30.m, "");
static_assert(&(p30_23->*deepm) == &t30.m, "");


/// Added tests not from constant-expression-cxx11.cpp
static_assert(pm, "");
static_assert(!((int S::*)nullptr), "");
constexpr int S::*pk = nullptr;
static_assert(!pk, "");
}

namespace test3 {
struct nsCSSRect {
};
static int nsCSSRect::* sides;
nsCSSRect dimenX;
void ParseBoxCornerRadii(int y) {
switch (y) {
}
int& x = dimenX.*sides;
}
}

void foo() {
class X;
void (X::*d) ();
d = nullptr; /// This calls in the constant interpreter.
}

namespace {
struct A { int n; };
struct B { int n; };
struct C : A, B {};
struct D { double d; C c; };
const int &&u = static_cast<B&&>(0, ((D&&)D{}).*&D::c).n; // both-warning {{left operand of comma operator has no effect}}
}

/// From SemaTemplate/instantiate-member-pointers.cpp
namespace {
struct Y {
int x;
};

template<typename T, typename Class, T Class::*Ptr>
struct X3 {
X3<T, Class, Ptr> &operator=(const T& value) {
return *this;
}
};

typedef int Y::*IntMember;
template<IntMember Member>
struct X4 {
X3<int, Y, Member> member;
int &getMember(Y& y) { return y.*Member; }
};

int &get_X4(X4<&Y::x> x4, Y& y) {
return x4.getMember(y);
}
}

/// From test/CXX/basic/basic.def.odr/p2.cpp
namespace {
void use(int);
struct S { int x; int f() const; };
constexpr S *ps = nullptr;
S *const &psr = ps;

void test() {
use(ps->*&S::x);
use(psr->*&S::x);
}
}
6 changes: 3 additions & 3 deletions clang/test/AST/ast-dump-default-init-json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -789,10 +789,10 @@ void test() {
// CHECK-NEXT: "valueCategory": "lvalue",
// CHECK-NEXT: "extendingDecl": {
// CHECK-NEXT: "id": "0x{{.*}}",
// CHECK-NEXT: "kind": "VarDecl",
// CHECK-NEXT: "name": "b",
// CHECK-NEXT: "kind": "FieldDecl",
// CHECK-NEXT: "name": "a",
// CHECK-NEXT: "type": {
// CHECK-NEXT: "qualType": "B"
// CHECK-NEXT: "qualType": "const A &"
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "storageDuration": "automatic",
Expand Down
2 changes: 1 addition & 1 deletion clang/test/AST/ast-dump-default-init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ void test() {
}
// CHECK: -CXXDefaultInitExpr 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue has rewritten init
// CHECK-NEXT: `-ExprWithCleanups 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue
// CHECK-NEXT: `-MaterializeTemporaryExpr 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue extended by Var 0x{{[^ ]*}} 'b' 'B'
// CHECK-NEXT: `-MaterializeTemporaryExpr 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue extended by Field 0x{{[^ ]*}} 'a' 'const A &'
// CHECK-NEXT: `-ImplicitCastExpr 0x{{[^ ]*}} <{{.*}}> 'const A' <NoOp>
// CHECK-NEXT: `-CXXFunctionalCastExpr 0x{{[^ ]*}} <{{.*}}> 'A' functional cast to A <NoOp>
// CHECK-NEXT: `-InitListExpr 0x{{[^ ]*}} <{{.*}}> 'A'
Expand Down
9 changes: 9 additions & 0 deletions clang/test/AST/ast-print-openacc-loop-construct.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,13 @@ void foo() {
// CHECK-NEXT: ;
#pragma acc loop auto
for(;;);

int i;
float array[5];

// CHECK: #pragma acc loop private(i, array[1], array, array[1:2])
// CHECK-NEXT: for (;;)
// CHECK-NEXT: ;
#pragma acc loop private(i, array[1], array, array[1:2])
for(;;);
}
12 changes: 6 additions & 6 deletions clang/test/Analysis/cxx-uninitialized-object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1114,27 +1114,27 @@ void fCXX11MemberInitTest1() {
CXX11MemberInitTest1();
}

#ifdef PEDANTIC
struct CXX11MemberInitTest2 {
struct RecordType {
int a; // expected-note {{uninitialized field 'this->a'}}
int b; // expected-note {{uninitialized field 'this->b'}}
// TODO: we'd expect the note: {{uninitialized field 'this->rec.a'}}
int a; // no-note
// TODO: we'd expect the note: {{uninitialized field 'this->rec.b'}}
int b; // no-note

RecordType(int) {}
};

RecordType rec = RecordType(int()); // expected-warning {{2 uninitialized fields}}
RecordType rec = RecordType(int());
int dontGetFilteredByNonPedanticMode = 0;

CXX11MemberInitTest2() {}
};

void fCXX11MemberInitTest2() {
// TODO: we'd expect the warning: {{2 uninitializeds field}}
CXX11MemberInitTest2(); // no-warning
}

#endif // PEDANTIC

//===----------------------------------------------------------------------===//
// "Esoteric" primitive type tests.
//===----------------------------------------------------------------------===//
Expand Down
10 changes: 5 additions & 5 deletions clang/test/Analysis/lifetime-extended-regions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,11 @@ void aggregateWithReferences() {
clang_analyzer_dump(viaReference); // expected-warning-re {{&lifetime_extended_object{RefAggregate, viaReference, S{{[0-9]+}}} }}
clang_analyzer_dump(viaReference.rx); // expected-warning-re {{&lifetime_extended_object{int, viaReference, S{{[0-9]+}}} }}
clang_analyzer_dump(viaReference.ry); // expected-warning-re {{&lifetime_extended_object{Composite, viaReference, S{{[0-9]+}}} }}
// The lifetime lifetime of object bound to reference members of aggregates,
// that are created from default member initializer was extended.
RefAggregate defaultInitExtended{i};
clang_analyzer_dump(defaultInitExtended.ry); // expected-warning-re {{&lifetime_extended_object{Composite, defaultInitExtended, S{{[0-9]+}}} }}

// clang does not currently implement extending lifetime of object bound to reference members of aggregates,
// that are created from default member initializer (see `warn_unsupported_lifetime_extension` from `-Wdangling`)
RefAggregate defaultInitExtended{i}; // clang-bug does not extend `Composite`
clang_analyzer_dump(defaultInitExtended.ry); // expected-warning {{Unknown }}
}

void lambda() {
Expand Down
2 changes: 2 additions & 0 deletions clang/test/CXX/drs/cwg16xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,8 @@ namespace cwg1696 { // cwg1696: 7
const A &a = A(); // #cwg1696-D1-a
};
D1 d1 = {}; // #cwg1696-d1
// since-cxx14-warning@-1 {{lifetime extension of temporary created by aggregate initialization using a default member initializer is not yet supported; lifetime of temporary will end at the end of the full-expression}}
// since-cxx14-note@#cwg1696-D1-a {{initializing field 'a' with default member initializer}}

struct D2 {
const A &a = A(); // #cwg1696-D2-a
Expand Down
19 changes: 5 additions & 14 deletions clang/test/CXX/drs/cwg18xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,28 +206,19 @@ namespace cwg1814 { // cwg1814: yes
#endif
}

namespace cwg1815 { // cwg1815: 19
namespace cwg1815 { // cwg1815: no
#if __cplusplus >= 201402L
struct A { int &&r = 0; };
// FIXME: needs codegen test
struct A { int &&r = 0; }; // #cwg1815-A
A a = {};
// since-cxx14-warning@-1 {{lifetime extension of temporary created by aggregate initialization using a default member initializer is not yet supported; lifetime of temporary will end at the end of the full-expression}} FIXME
// since-cxx14-note@#cwg1815-A {{initializing field 'r' with default member initializer}}

struct B { int &&r = 0; }; // #cwg1815-B
// since-cxx14-error@-1 {{reference member 'r' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
// since-cxx14-note@#cwg1815-B {{initializing field 'r' with default member initializer}}
// since-cxx14-note@#cwg1815-b {{in implicit default constructor for 'cwg1815::B' first required here}}
B b; // #cwg1815-b

#if __cplusplus >= 201703L
struct C { const int &r = 0; };
constexpr C c = {}; // OK, since cwg1815
static_assert(c.r == 0);

constexpr int f() {
A a = {}; // OK, since cwg1815
return a.r;
}
static_assert(f() == 0);
#endif
#endif
}

Expand Down
34 changes: 0 additions & 34 deletions clang/test/CXX/special/class.temporary/p6.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -269,40 +269,6 @@ void init_capture_init_list() {
// CHECK: }
}

void check_dr1815() { // dr1815: yes
#if __cplusplus >= 201402L

struct A {
int &&r = 0;
~A() {}
};

struct B {
A &&a = A{};
~B() {}
};
B a = {};

// CHECK: call {{.*}}block_scope_begin_function
extern void block_scope_begin_function();
extern void block_scope_end_function();
block_scope_begin_function();
{
// CHECK: call void @_ZZ12check_dr1815vEN1BD1Ev
// CHECK: call void @_ZZ12check_dr1815vEN1AD1Ev
B b = {};
}
// CHECK: call {{.*}}block_scope_end_function
block_scope_end_function();

// CHECK: call {{.*}}some_other_function
extern void some_other_function();
some_other_function();
// CHECK: call void @_ZZ12check_dr1815vEN1BD1Ev
// CHECK: call void @_ZZ12check_dr1815vEN1AD1Ev
#endif
}

namespace P2718R0 {
namespace basic {
template <typename E> using T2 = std::list<E>;
Expand Down
Loading