134 changes: 0 additions & 134 deletions clang-tools-extra/test/clang-tidy/checkers/cert/dcl21-cpp.cpp

This file was deleted.

4 changes: 2 additions & 2 deletions clang/docs/HIPSupport.rst
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,8 @@ Predefined Macros
* - ``HIP_API_PER_THREAD_DEFAULT_STREAM``
- Alias to ``__HIP_API_PER_THREAD_DEFAULT_STREAM__``. Deprecated.

Note that some architecture specific AMDGPU macros will have default values when
used from the HIP host compilation. Other :doc:`AMDGPU macros <AMDGPUSupport>`
Note that some architecture specific AMDGPU macros will have default values when
used from the HIP host compilation. Other :doc:`AMDGPU macros <AMDGPUSupport>`
like ``__AMDGCN_WAVEFRONT_SIZE__`` will default to 64 for example.

Compilation Modes
Expand Down
12 changes: 8 additions & 4 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,9 @@ Bug Fixes to C++ Support

- Fix crash when using lifetimebound attribute in function with trailing return.
Fixes (`#73619 <https://github.com/llvm/llvm-project/issues/73619>`_)
- Fix a crash when specializing an out-of-line member function with a default
parameter where we did an incorrect specialization of the initialization of
the default parameter.
Fixes (`#68490 <https://github.com/llvm/llvm-project/issues/68490>`_)
- Addressed an issue where constraints involving injected class types are perceived
distinct from its specialization types.
(`#56482 <https://github.com/llvm/llvm-project/issues/56482>`_)
- Fixed a bug where variables referenced by requires-clauses inside
nested generic lambdas were not properly injected into the constraint scope.
(`#73418 <https://github.com/llvm/llvm-project/issues/73418>`_)
Expand All @@ -180,6 +179,8 @@ Bug Fixes to C++ Support
- Fix for crash when using a erroneous type in a return statement.
Fixes (`#63244 <https://github.com/llvm/llvm-project/issues/63244>`_)
and (`#79745 <https://github.com/llvm/llvm-project/issues/79745>`_)
- Fix incorrect code generation caused by the object argument of ``static operator()`` and ``static operator[]`` calls not being evaluated.
Fixes (`#67976 <https://github.com/llvm/llvm-project/issues/67976>`_)

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -263,6 +264,9 @@ Improvements

- Support importing C++20 modules in clang-repl.

- Added support for ``TypeLoc::dump()`` for easier debugging, and improved
textual and JSON dumping for various ``TypeLoc``-related nodes.

Moved checkers
^^^^^^^^^^^^^^

Expand Down
2 changes: 0 additions & 2 deletions clang/docs/tools/clang-formatted-files.txt
Original file line number Diff line number Diff line change
Expand Up @@ -985,8 +985,6 @@ clang-tools-extra/clang-tidy/cert/MutatingCopyCheck.cpp
clang-tools-extra/clang-tidy/cert/MutatingCopyCheck.h
clang-tools-extra/clang-tidy/cert/NonTrivialTypesLibcMemoryCallsCheck.cpp
clang-tools-extra/clang-tidy/cert/NonTrivialTypesLibcMemoryCallsCheck.h
clang-tools-extra/clang-tidy/cert/PostfixOperatorCheck.cpp
clang-tools-extra/clang-tidy/cert/PostfixOperatorCheck.h
clang-tools-extra/clang-tidy/cert/ProperlySeededRandomGeneratorCheck.cpp
clang-tools-extra/clang-tidy/cert/ProperlySeededRandomGeneratorCheck.h
clang-tools-extra/clang-tidy/cert/SetLongJmpCheck.cpp
Expand Down
82 changes: 79 additions & 3 deletions clang/include/clang/AST/ASTNodeTraverser.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/TemplateArgumentVisitor.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLocVisitor.h"
#include "clang/AST/TypeVisitor.h"
#include "llvm/Support/SaveAndRestore.h"

namespace clang {

Expand All @@ -48,6 +50,7 @@ struct {
void Visit(const Stmt *Node);
void Visit(const Type *T);
void Visit(QualType T);
void Visit(TypeLoc);
void Visit(const Decl *D);
void Visit(const CXXCtorInitializer *Init);
void Visit(const OMPClause *C);
Expand All @@ -64,13 +67,22 @@ class ASTNodeTraverser
public comments::ConstCommentVisitor<Derived, void,
const comments::FullComment *>,
public TypeVisitor<Derived>,
public TypeLocVisitor<Derived>,
public ConstAttrVisitor<Derived>,
public ConstTemplateArgumentVisitor<Derived> {

/// Indicates whether we should trigger deserialization of nodes that had
/// not already been loaded.
bool Deserialize = false;

/// Tracks whether we should dump TypeLocs etc.
///
/// Detailed location information such as TypeLoc nodes is not usually
/// included in the dump (too verbose).
/// But when explicitly asked to dump a Loc node, we do so recursively,
/// including e.g. FunctionTypeLoc => ParmVarDecl => TypeLoc.
bool VisitLocs = false;

TraversalKind Traversal = TraversalKind::TK_AsIs;

NodeDelegateType &getNodeDelegate() {
Expand All @@ -85,7 +97,7 @@ class ASTNodeTraverser
void SetTraversalKind(TraversalKind TK) { Traversal = TK; }
TraversalKind GetTraversalKind() const { return Traversal; }

void Visit(const Decl *D) {
void Visit(const Decl *D, bool VisitLocs = false) {
if (Traversal == TK_IgnoreUnlessSpelledInSource && D->isImplicit())
return;

Expand All @@ -94,7 +106,10 @@ class ASTNodeTraverser
if (!D)
return;

ConstDeclVisitor<Derived>::Visit(D);
{
llvm::SaveAndRestore RestoreVisitLocs(this->VisitLocs, VisitLocs);
ConstDeclVisitor<Derived>::Visit(D);
}

for (const auto &A : D->attrs())
Visit(A);
Expand Down Expand Up @@ -181,6 +196,17 @@ class ASTNodeTraverser
});
}

void Visit(TypeLoc T) {
getNodeDelegate().AddChild([=] {
getNodeDelegate().Visit(T);
if (T.isNull())
return;
TypeLocVisitor<Derived>::Visit(T);
if (auto Inner = T.getNextTypeLoc())
Visit(Inner);
});
}

void Visit(const Attr *A) {
getNodeDelegate().AddChild([=] {
getNodeDelegate().Visit(A);
Expand Down Expand Up @@ -286,6 +312,8 @@ class ASTNodeTraverser
Visit(*QT);
else if (const auto *T = N.get<Type>())
Visit(T);
else if (const auto *TL = N.get<TypeLoc>())
Visit(*TL);
else if (const auto *C = N.get<CXXCtorInitializer>())
Visit(C);
else if (const auto *C = N.get<OMPClause>())
Expand Down Expand Up @@ -346,7 +374,7 @@ class ASTNodeTraverser

void VisitComplexType(const ComplexType *T) { Visit(T->getElementType()); }
void VisitLocInfoType(const LocInfoType *T) {
Visit(T->getTypeSourceInfo()->getType());
Visit(T->getTypeSourceInfo()->getTypeLoc());
}
void VisitPointerType(const PointerType *T) { Visit(T->getPointeeType()); }
void VisitBlockPointerType(const BlockPointerType *T) {
Expand Down Expand Up @@ -421,9 +449,55 @@ class ASTNodeTraverser
if (!T->isSugared())
Visit(T->getPattern());
}
void VisitAutoType(const AutoType *T) {
for (const auto &Arg : T->getTypeConstraintArguments())
Visit(Arg);
}
// FIXME: ElaboratedType, DependentNameType,
// DependentTemplateSpecializationType, ObjCObjectType

// For TypeLocs, we automatically visit the inner type loc (pointee type etc).
// We must explicitly visit other lexically-nested nodes.
void VisitFunctionProtoTypeLoc(FunctionProtoTypeLoc TL) {
TypeLocVisitor<Derived>::VisitFunctionTypeLoc(TL);
for (const auto *Param : TL.getParams())
Visit(Param, /*VisitTypeLocs=*/true);
}
void VisitAutoTypeLoc(AutoTypeLoc TL) {
if (const auto *CR = TL.getConceptReference()) {
if (auto *Args = CR->getTemplateArgsAsWritten())
for (const auto &Arg : Args->arguments())
dumpTemplateArgumentLoc(Arg);
}
}
void VisitMemberPointerTypeLoc(MemberPointerTypeLoc TL) {
Visit(TL.getClassTInfo()->getTypeLoc());
}
void VisitVariableArrayTypeLoc(VariableArrayTypeLoc TL) {
Visit(TL.getSizeExpr());
}
void VisitDependentSizedArrayTypeLoc(DependentSizedArrayTypeLoc TL) {
Visit(TL.getSizeExpr());
}
void VisitDependentSizedExtVectorTypeLoc(DependentSizedExtVectorTypeLoc TL) {
Visit(cast<DependentSizedExtVectorType>(TL.getType())->getSizeExpr());
}
void VisitTypeOfExprTypeLoc(TypeOfExprTypeLoc TL) {
Visit(TL.getUnderlyingExpr());
}
void VisitDecltypeType(DecltypeType TL) {
Visit(TL.getUnderlyingExpr());
}
void VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc TL) {
for (unsigned I=0, N=TL.getNumArgs(); I < N; ++I)
dumpTemplateArgumentLoc(TL.getArgLoc(I));
}
void VisitDependentTemplateSpecializationTypeLoc(
DependentTemplateSpecializationTypeLoc TL) {
for (unsigned I=0, N=TL.getNumArgs(); I < N; ++I)
dumpTemplateArgumentLoc(TL.getArgLoc(I));
}

void VisitTypedefDecl(const TypedefDecl *D) { Visit(D->getUnderlyingType()); }

void VisitEnumConstantDecl(const EnumConstantDecl *D) {
Expand Down Expand Up @@ -468,6 +542,8 @@ class ASTNodeTraverser
if (Traversal == TK_IgnoreUnlessSpelledInSource && D->isCXXForRangeDecl())
return;

if (const auto *TSI = D->getTypeSourceInfo(); VisitLocs && TSI)
Visit(TSI->getTypeLoc());
if (D->hasInit())
Visit(D->getInit());
}
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/AST/JSONNodeDumper.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ class JSONNodeDumper
void Visit(const Type *T);
void Visit(QualType T);
void Visit(const Decl *D);
void Visit(TypeLoc TL);

void Visit(const comments::Comment *C, const comments::FullComment *FC);
void Visit(const TemplateArgument &TA, SourceRange R = {},
Expand All @@ -207,6 +208,7 @@ class JSONNodeDumper
void Visit(const GenericSelectionExpr::ConstAssociation &A);
void Visit(const concepts::Requirement *R);
void Visit(const APValue &Value, QualType Ty);
void Visit(const ConceptReference *);

void VisitAliasAttr(const AliasAttr *AA);
void VisitCleanupAttr(const CleanupAttr *CA);
Expand Down
40 changes: 40 additions & 0 deletions clang/include/clang/AST/OpenMPClause.h
Original file line number Diff line number Diff line change
Expand Up @@ -2513,6 +2513,46 @@ class OMPRelaxedClause final : public OMPClause {
}
};

/// This represents 'weak' clause in the '#pragma omp atomic'
/// directives.
///
/// \code
/// #pragma omp atomic compare weak
/// \endcode
/// In this example directive '#pragma omp atomic' has 'weak' clause.
class OMPWeakClause final : public OMPClause {
public:
/// Build 'weak' clause.
///
/// \param StartLoc Starting location of the clause.
/// \param EndLoc Ending location of the clause.
OMPWeakClause(SourceLocation StartLoc, SourceLocation EndLoc)
: OMPClause(llvm::omp::OMPC_weak, StartLoc, EndLoc) {}

/// Build an empty clause.
OMPWeakClause()
: OMPClause(llvm::omp::OMPC_weak, SourceLocation(), SourceLocation()) {}

child_range children() {
return child_range(child_iterator(), child_iterator());
}

const_child_range children() const {
return const_child_range(const_child_iterator(), const_child_iterator());
}

child_range used_children() {
return child_range(child_iterator(), child_iterator());
}
const_child_range used_children() const {
return const_child_range(const_child_iterator(), const_child_iterator());
}

static bool classof(const OMPClause *T) {
return T->getClauseKind() == llvm::omp::OMPC_weak;
}
};

/// This represents 'fail' clause in the '#pragma omp atomic'
/// directive.
///
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -3436,6 +3436,11 @@ bool RecursiveASTVisitor<Derived>::VisitOMPRelaxedClause(OMPRelaxedClause *) {
return true;
}

template <typename Derived>
bool RecursiveASTVisitor<Derived>::VisitOMPWeakClause(OMPWeakClause *) {
return true;
}

template <typename Derived>
bool RecursiveASTVisitor<Derived>::VisitOMPThreadsClause(OMPThreadsClause *) {
return true;
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/AST/TextNodeDumper.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/TemplateArgumentVisitor.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLocVisitor.h"
#include "clang/AST/TypeVisitor.h"

namespace clang {
Expand Down Expand Up @@ -132,6 +133,7 @@ class TextNodeDumper
public ConstTemplateArgumentVisitor<TextNodeDumper>,
public ConstStmtVisitor<TextNodeDumper>,
public TypeVisitor<TextNodeDumper>,
public TypeLocVisitor<TextNodeDumper>,
public ConstDeclVisitor<TextNodeDumper> {
raw_ostream &OS;
const bool ShowColors;
Expand Down Expand Up @@ -179,6 +181,8 @@ class TextNodeDumper

void Visit(QualType T);

void Visit(TypeLoc);

void Visit(const Decl *D);

void Visit(const CXXCtorInitializer *Init);
Expand Down Expand Up @@ -339,6 +343,8 @@ class TextNodeDumper
void VisitObjCInterfaceType(const ObjCInterfaceType *T);
void VisitPackExpansionType(const PackExpansionType *T);

void VisitTypeLoc(TypeLoc TL);

void VisitLabelDecl(const LabelDecl *D);
void VisitTypedefDecl(const TypedefDecl *D);
void VisitEnumDecl(const EnumDecl *D);
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/AST/TypeLoc.h
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,9 @@ class TypeLoc {
/// __nullable, or __null_unspecifier), if there is one.
SourceLocation findNullabilityLoc() const;

void dump() const;
void dump(llvm::raw_ostream &, const ASTContext &) const;

private:
static bool isKind(const TypeLoc&) {
return true;
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticDriverKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ def warn_drv_avr_stdlib_not_linked: Warning<
InGroup<AVRRtlibLinkingQuirks>;
def err_drv_cuda_bad_gpu_arch : Error<"unsupported CUDA gpu architecture: %0">;
def err_drv_offload_bad_gpu_arch : Error<"unsupported %0 gpu architecture: %1">;
def err_drv_offload_missing_gpu_arch : Error<
"Must pass in an explicit %0 gpu architecture to '%1'">;
def err_drv_no_cuda_installation : Error<
"cannot find CUDA installation; provide its path via '--cuda-path', or pass "
"'-nocudainc' to build without CUDA includes">;
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -11042,7 +11042,8 @@ def note_omp_atomic_compare: Note<
"expect lvalue for result value|expect scalar value|expect integer value|unexpected 'else' statement|expect '==' operator|expect an assignment statement 'v = x'|"
"expect a 'if' statement|expect no more than two statements|expect a compound statement|expect 'else' statement|expect a form 'r = x == e; if (r) ...'}0">;
def err_omp_atomic_fail_wrong_or_no_clauses : Error<"expected a memory order clause">;
def err_omp_atomic_fail_no_compare : Error<"expected 'compare' clause with the 'fail' modifier">;
def err_omp_atomic_no_compare : Error<"expected 'compare' clause with the '%0' modifier">;
def err_omp_atomic_weak_no_equality : Error<"expected '==' operator for 'weak' clause">;
def err_omp_atomic_several_clauses : Error<
"directive '#pragma omp atomic' cannot contain more than one 'read', 'write', 'update', 'capture', or 'compare' clause">;
def err_omp_several_mem_order_clauses : Error<
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -12377,6 +12377,9 @@ class Sema final {
/// Called on well-formed 'relaxed' clause.
OMPClause *ActOnOpenMPRelaxedClause(SourceLocation StartLoc,
SourceLocation EndLoc);
/// Called on well-formed 'weak' clause.
OMPClause *ActOnOpenMPWeakClause(SourceLocation StartLoc,
SourceLocation EndLoc);

/// Called on well-formed 'init' clause.
OMPClause *
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "llvm/ADT/SmallVector.h"

namespace clang {
class ASTContext;
class Decl;

namespace ento {
Expand All @@ -27,6 +28,8 @@ class PathDiagnosticLocation;

class BugSuppression {
public:
explicit BugSuppression(const ASTContext &ACtx) : ACtx(ACtx) {}

using DiagnosticIdentifierList = llvm::ArrayRef<llvm::StringRef>;

/// Return true if the given bug report was explicitly suppressed by the user.
Expand All @@ -45,6 +48,8 @@ class BugSuppression {
llvm::SmallVector<SourceRange, EXPECTED_NUMBER_OF_SUPPRESSIONS>;

llvm::DenseMap<const Decl *, CachedRanges> CachedSuppressionLocations;

const ASTContext &ACtx;
};

} // end namespace ento
Expand Down
13 changes: 13 additions & 0 deletions clang/lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,19 @@ LLVM_DUMP_METHOD void Type::dump(llvm::raw_ostream &OS,
QualType(this, 0).dump(OS, Context);
}

//===----------------------------------------------------------------------===//
// TypeLoc method implementations
//===----------------------------------------------------------------------===//

LLVM_DUMP_METHOD void TypeLoc::dump() const {
ASTDumper(llvm::errs(), /*ShowColors=*/false).Visit(*this);
}

LLVM_DUMP_METHOD void TypeLoc::dump(llvm::raw_ostream &OS,
const ASTContext &Context) const {
ASTDumper(OS, Context, Context.getDiagnostics().getShowColors()).Visit(*this);
}

//===----------------------------------------------------------------------===//
// Decl method implementations
//===----------------------------------------------------------------------===//
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/ASTTypeTraits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,8 @@ void DynTypedNode::dump(llvm::raw_ostream &OS,
T->dump(OS, Context);
else if (const ConceptReference *C = get<ConceptReference>())
C->dump(OS);
else if (const TypeLoc *TL = get<TypeLoc>())
TL->dump(OS, Context);
else
OS << "Unable to dump values of type " << NodeKind.asStringRef() << "\n";
}
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/DeclTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1583,6 +1583,10 @@ void TemplateParamObjectDecl::printAsInit(llvm::raw_ostream &OS,

TemplateParameterList *clang::getReplacedTemplateParameterList(Decl *D) {
switch (D->getKind()) {
case Decl::Kind::CXXRecord:
return cast<CXXRecordDecl>(D)
->getDescribedTemplate()
->getTemplateParameters();
case Decl::Kind::ClassTemplate:
return cast<ClassTemplateDecl>(D)->getTemplateParameters();
case Decl::Kind::ClassTemplateSpecialization: {
Expand Down
9 changes: 7 additions & 2 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7951,7 +7951,8 @@ class ExprEvaluatorBase
// Overloaded operator calls to member functions are represented as normal
// calls with '*this' as the first argument.
const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD);
if (MD && MD->isImplicitObjectMemberFunction()) {
if (MD &&
(MD->isImplicitObjectMemberFunction() || (OCE && MD->isStatic()))) {
// FIXME: When selecting an implicit conversion for an overloaded
// operator delete, we sometimes try to evaluate calls to conversion
// operators without a 'this' parameter!
Expand All @@ -7960,7 +7961,11 @@ class ExprEvaluatorBase

if (!EvaluateObjectArgument(Info, Args[0], ThisVal))
return false;
This = &ThisVal;

// If we are calling a static operator, the 'this' argument needs to be
// ignored after being evaluated.
if (MD->isInstance())
This = &ThisVal;

// If this is syntactically a simple assignment using a trivial
// assignment operator, start the lifetimes of union members as needed,
Expand Down
27 changes: 24 additions & 3 deletions clang/lib/AST/Interp/ByteCodeEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "ByteCodeGenError.h"
#include "Context.h"
#include "Floating.h"
#include "IntegralAP.h"
#include "Opcode.h"
#include "Program.h"
#include "clang/AST/ASTLambda.h"
Expand Down Expand Up @@ -209,9 +210,11 @@ static void emit(Program &P, std::vector<std::byte> &Code, const T &Val,
}
}

template <>
void emit(Program &P, std::vector<std::byte> &Code, const Floating &Val,
bool &Success) {
/// Emits a serializable value. These usually (potentially) contain
/// heap-allocated memory and aren't trivially copyable.
template <typename T>
static void emitSerialized(std::vector<std::byte> &Code, const T &Val,
bool &Success) {
size_t Size = Val.bytesToSerialize();

if (Code.size() + Size > std::numeric_limits<unsigned>::max()) {
Expand All @@ -228,6 +231,24 @@ void emit(Program &P, std::vector<std::byte> &Code, const Floating &Val,
Val.serialize(Code.data() + ValPos);
}

template <>
void emit(Program &P, std::vector<std::byte> &Code, const Floating &Val,
bool &Success) {
emitSerialized(Code, Val, Success);
}

template <>
void emit(Program &P, std::vector<std::byte> &Code,
const IntegralAP<false> &Val, bool &Success) {
emitSerialized(Code, Val, Success);
}

template <>
void emit(Program &P, std::vector<std::byte> &Code, const IntegralAP<true> &Val,
bool &Success) {
emitSerialized(Code, Val, Success);
}

template <typename... Tys>
bool ByteCodeEmitter::emitOp(Opcode Op, const Tys &... Args, const SourceInfo &SI) {
bool Success = true;
Expand Down
85 changes: 79 additions & 6 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,63 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
return this->emitInitElem(T, 1, SubExpr);
}

case CK_IntegralComplexCast:
case CK_FloatingComplexCast:
case CK_IntegralComplexToFloatingComplex:
case CK_FloatingComplexToIntegralComplex: {
assert(CE->getType()->isAnyComplexType());
assert(SubExpr->getType()->isAnyComplexType());
if (DiscardResult)
return this->discard(SubExpr);

if (!Initializing) {
std::optional<unsigned> LocalIndex =
allocateLocal(CE, /*IsExtended=*/true);
if (!LocalIndex)
return false;
if (!this->emitGetPtrLocal(*LocalIndex, CE))
return false;
}

// Location for the SubExpr.
// Since SubExpr is of complex type, visiting it results in a pointer
// anyway, so we just create a temporary pointer variable.
std::optional<unsigned> SubExprOffset = allocateLocalPrimitive(
SubExpr, PT_Ptr, /*IsConst=*/true, /*IsExtended=*/false);
if (!SubExprOffset)
return false;

if (!this->visit(SubExpr))
return false;
if (!this->emitSetLocal(PT_Ptr, *SubExprOffset, CE))
return false;

PrimType SourceElemT = classifyComplexElementType(SubExpr->getType());
QualType DestElemType =
CE->getType()->getAs<ComplexType>()->getElementType();
PrimType DestElemT = classifyPrim(DestElemType);
// Cast both elements individually.
for (unsigned I = 0; I != 2; ++I) {
if (!this->emitGetLocal(PT_Ptr, *SubExprOffset, CE))
return false;
if (!this->emitConstUint8(I, CE))
return false;
if (!this->emitArrayElemPtrPopUint8(CE))
return false;
if (!this->emitLoadPop(SourceElemT, CE))
return false;

// Do the cast.
if (!this->emitPrimCast(SourceElemT, DestElemT, DestElemType, CE))
return false;

// Save the value.
if (!this->emitInitElem(DestElemT, I, CE))
return false;
}
return true;
}

case CK_ToVoid:
return discard(SubExpr);

Expand Down Expand Up @@ -826,7 +883,7 @@ bool ByteCodeExprGen<Emitter>::visitArrayElemInit(unsigned ElemIndex,
return false;
if (!this->visitInitializer(Init))
return false;
return this->emitPopPtr(Init);
return this->emitInitPtrPop(Init);
}

template <class Emitter>
Expand Down Expand Up @@ -854,13 +911,26 @@ bool ByteCodeExprGen<Emitter>::VisitInitListExpr(const InitListExpr *E) {
return this->visitInitList(E->inits(), E);

if (T->isArrayType()) {
// FIXME: Array fillers.
unsigned ElementIndex = 0;
for (const Expr *Init : E->inits()) {
if (!this->visitArrayElemInit(ElementIndex, Init))
return false;
++ElementIndex;
}

// Expand the filler expression.
// FIXME: This should go away.
if (const Expr *Filler = E->getArrayFiller()) {
const ConstantArrayType *CAT =
Ctx.getASTContext().getAsConstantArrayType(E->getType());
uint64_t NumElems = CAT->getSize().getZExtValue();

for (; ElementIndex != NumElems; ++ElementIndex) {
if (!this->visitArrayElemInit(ElementIndex, Filler))
return false;
}
}

return true;
}

Expand Down Expand Up @@ -2191,15 +2261,13 @@ bool ByteCodeExprGen<Emitter>::emitConst(T Value, PrimType Ty, const Expr *E) {
return this->emitConstSint64(Value, E);
case PT_Uint64:
return this->emitConstUint64(Value, E);
case PT_IntAP:
case PT_IntAPS:
assert(false);
return false;
case PT_Bool:
return this->emitConstBool(Value, E);
case PT_Ptr:
case PT_FnPtr:
case PT_Float:
case PT_IntAP:
case PT_IntAPS:
llvm_unreachable("Invalid integral type");
break;
}
Expand All @@ -2215,6 +2283,11 @@ bool ByteCodeExprGen<Emitter>::emitConst(T Value, const Expr *E) {
template <class Emitter>
bool ByteCodeExprGen<Emitter>::emitConst(const APSInt &Value, PrimType Ty,
const Expr *E) {
if (Ty == PT_IntAPS)
return this->emitConstIntAPS(Value, E);
if (Ty == PT_IntAP)
return this->emitConstIntAP(Value, E);

if (Value.isSigned())
return this->emitConst(Value.getSExtValue(), Ty, E);
return this->emitConst(Value.getZExtValue(), Ty, E);
Expand Down
17 changes: 8 additions & 9 deletions clang/lib/AST/Interp/ByteCodeExprGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,15 +128,8 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
// If the function does not exist yet, it is compiled.
const Function *getFunction(const FunctionDecl *FD);

/// Classifies a type.
std::optional<PrimType> classify(const Expr *E) const {
if (E->isGLValue()) {
if (E->getType()->isFunctionType())
return PT_FnPtr;
return PT_Ptr;
}

return classify(E->getType());
return Ctx.classify(E);
}
std::optional<PrimType> classify(QualType Ty) const {
return Ctx.classify(Ty);
Expand Down Expand Up @@ -180,6 +173,9 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
if (!visitInitializer(Init))
return false;

if (!this->emitInitPtr(Init))
return false;

return this->emitPopPtr(Init);
}

Expand All @@ -191,6 +187,9 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
if (!visitInitializer(Init))
return false;

if (!this->emitInitPtr(Init))
return false;

return this->emitPopPtr(Init);
}

Expand All @@ -202,7 +201,7 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
if (!visitInitializer(I))
return false;

return this->emitPopPtr(I);
return this->emitInitPtrPop(I);
}

bool visitInitList(ArrayRef<const Expr *> Inits, const Expr *E);
Expand Down
13 changes: 12 additions & 1 deletion clang/lib/AST/Interp/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,20 @@ class Context final {
/// Return the size of T in bits.
uint32_t getBitWidth(QualType T) const { return Ctx.getIntWidth(T); }

/// Classifies an expression.
/// Classifies a type.
std::optional<PrimType> classify(QualType T) const;

/// Classifies an expression.
std::optional<PrimType> classify(const Expr *E) const {
if (E->isGLValue()) {
if (E->getType()->isFunctionType())
return PT_FnPtr;
return PT_Ptr;
}

return classify(E->getType());
}

const CXXMethodDecl *
getOverridingFunction(const CXXRecordDecl *DynamicDecl,
const CXXRecordDecl *StaticDecl,
Expand Down
25 changes: 13 additions & 12 deletions clang/lib/AST/Interp/Descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,18 +243,19 @@ Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
bool IsMutable)
: Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems),
MDSize(MD.value_or(0)),
AllocSize(align(Size) + sizeof(InitMapPtr) + MDSize), IsConst(IsConst),
IsMutable(IsMutable), IsTemporary(IsTemporary), IsArray(true),
CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)),
MoveFn(getMoveArrayPrim(Type)) {
AllocSize(align(MDSize) + align(Size) + sizeof(InitMapPtr)),
IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary),
IsArray(true), CtorFn(getCtorArrayPrim(Type)),
DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) {
assert(Source && "Missing source");
}

/// Primitive unknown-size arrays.
Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary,
UnknownSize)
: Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark), MDSize(0),
AllocSize(alignof(void *) + sizeof(InitMapPtr)), IsConst(true),
Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
bool IsTemporary, UnknownSize)
: Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark),
MDSize(MD.value_or(0)),
AllocSize(MDSize + sizeof(InitMapPtr) + alignof(void *)), IsConst(true),
IsMutable(false), IsTemporary(IsTemporary), IsArray(true),
CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)),
MoveFn(getMoveArrayPrim(Type)) {
Expand All @@ -275,12 +276,12 @@ Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD,
}

/// Unknown-size arrays of composite elements.
Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem,
Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD,
bool IsTemporary, UnknownSize)
: Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)),
Size(UnknownSizeMark), MDSize(0),
AllocSize(alignof(void *) + sizeof(InitMapPtr)), ElemDesc(Elem),
IsConst(true), IsMutable(false), IsTemporary(IsTemporary), IsArray(true),
Size(UnknownSizeMark), MDSize(MD.value_or(0)),
AllocSize(MDSize + alignof(void *)), ElemDesc(Elem), IsConst(true),
IsMutable(false), IsTemporary(IsTemporary), IsArray(true),
CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) {
assert(Source && "Missing source");
}
Expand Down
11 changes: 8 additions & 3 deletions clang/lib/AST/Interp/Descriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ struct InlineDescriptor {
unsigned IsFieldMutable : 1;

const Descriptor *Desc;

InlineDescriptor(const Descriptor *D)
: Offset(sizeof(InlineDescriptor)), IsConst(false), IsInitialized(false),
IsBase(false), IsActive(false), IsFieldMutable(false), Desc(D) {}
};

/// Describes a memory block created by an allocation site.
Expand Down Expand Up @@ -128,15 +132,16 @@ struct Descriptor final {
bool IsConst, bool IsTemporary, bool IsMutable);

/// Allocates a descriptor for an array of primitives of unknown size.
Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary, UnknownSize);
Descriptor(const DeclTy &D, PrimType Type, MetadataSize MDSize,
bool IsTemporary, UnknownSize);

/// Allocates a descriptor for an array of composites.
Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD,
unsigned NumElems, bool IsConst, bool IsTemporary, bool IsMutable);

/// Allocates a descriptor for an array of composites of unknown size.
Descriptor(const DeclTy &D, const Descriptor *Elem, bool IsTemporary,
UnknownSize);
Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD,
bool IsTemporary, UnknownSize);

/// Allocates a descriptor for a record.
Descriptor(const DeclTy &D, const Record *R, MetadataSize MD, bool IsConst,
Expand Down
15 changes: 15 additions & 0 deletions clang/lib/AST/Interp/Disasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "Floating.h"
#include "Function.h"
#include "IntegralAP.h"
#include "Opcode.h"
#include "PrimType.h"
#include "Program.h"
Expand All @@ -37,6 +38,20 @@ template <> inline Floating ReadArg<Floating>(Program &P, CodePtr &OpPC) {
return F;
}

template <>
inline IntegralAP<false> ReadArg<IntegralAP<false>>(Program &P, CodePtr &OpPC) {
IntegralAP<false> I = IntegralAP<false>::deserialize(*OpPC);
OpPC += align(I.bytesToSerialize());
return I;
}

template <>
inline IntegralAP<true> ReadArg<IntegralAP<true>>(Program &P, CodePtr &OpPC) {
IntegralAP<true> I = IntegralAP<true>::deserialize(*OpPC);
OpPC += align(I.bytesToSerialize());
return I;
}

LLVM_DUMP_METHOD void Function::dump() const { dump(llvm::errs()); }

LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS) const {
Expand Down
30 changes: 30 additions & 0 deletions clang/lib/AST/Interp/IntegralAP.h
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,31 @@ template <bool Signed> class IntegralAP final {
*R = IntegralAP(A.V.lshr(ShiftAmount));
}

// === Serialization support ===
size_t bytesToSerialize() const {
// 4 bytes for the BitWidth followed by N bytes for the actual APInt.
return sizeof(uint32_t) + (V.getBitWidth() / CHAR_BIT);
}

void serialize(std::byte *Buff) const {
assert(V.getBitWidth() < std::numeric_limits<uint8_t>::max());
uint32_t BitWidth = V.getBitWidth();

std::memcpy(Buff, &BitWidth, sizeof(uint32_t));
llvm::StoreIntToMemory(V, (uint8_t *)(Buff + sizeof(uint32_t)),
BitWidth / CHAR_BIT);
}

static IntegralAP<Signed> deserialize(const std::byte *Buff) {
uint32_t BitWidth;
std::memcpy(&BitWidth, Buff, sizeof(uint32_t));
IntegralAP<Signed> Val(APInt(BitWidth, 0ull, !Signed));

llvm::LoadIntFromMemory(Val.V, (const uint8_t *)Buff + sizeof(uint32_t),
BitWidth / CHAR_BIT);
return Val;
}

private:
template <template <typename T> class Op>
static bool CheckAddSubMulUB(const IntegralAP &A, const IntegralAP &B,
Expand All @@ -289,6 +314,11 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
return OS;
}

template <bool Signed>
IntegralAP<Signed> getSwappedBytes(IntegralAP<Signed> F) {
return F;
}

} // namespace interp
} // namespace clang

Expand Down
19 changes: 18 additions & 1 deletion clang/lib/AST/Interp/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ static bool CheckGlobal(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
namespace clang {
namespace interp {
static void popArg(InterpState &S, const Expr *Arg) {
PrimType Ty = S.getContext().classify(Arg->getType()).value_or(PT_Ptr);
PrimType Ty = S.getContext().classify(Arg).value_or(PT_Ptr);
TYPE_SWITCH(Ty, S.Stk.discard<T>());
}

Expand Down Expand Up @@ -356,6 +356,23 @@ bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
return false;
}

bool CheckGlobalInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
if (Ptr.isInitialized())
return true;

assert(S.getLangOpts().CPlusPlus);
const auto *VD = cast<VarDecl>(Ptr.getDeclDesc()->asValueDecl());
if ((!VD->hasConstantInitialization() &&
VD->mightBeUsableInConstantExpressions(S.getCtx())) ||
(S.getLangOpts().OpenCL && !S.getLangOpts().CPlusPlus11 &&
!VD->hasICEInitializer(S.getCtx()))) {
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.FFDiag(Loc, diag::note_constexpr_var_init_non_constant, 1) << VD;
S.Note(VD->getLocation(), diag::note_declared_at);
}
return false;
}

bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
if (!CheckLive(S, OpPC, Ptr, AK_Read))
return false;
Expand Down
56 changes: 45 additions & 11 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr);

bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
AccessKinds AK);
/// Check if a global variable is initialized.
bool CheckGlobalInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr);

/// Checks if a value can be stored in a block.
bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
Expand Down Expand Up @@ -1003,13 +1005,18 @@ bool SetThisField(InterpState &S, CodePtr OpPC, uint32_t I) {

template <PrimType Name, class T = typename PrimConv<Name>::T>
bool GetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
const Block *B = S.P.getGlobal(I);

if (!CheckConstant(S, OpPC, B->getDescriptor()))
const Pointer &Ptr = S.P.getPtrGlobal(I);
if (!CheckConstant(S, OpPC, Ptr.getFieldDesc()))
return false;
if (B->isExtern())
if (Ptr.isExtern())
return false;
S.Stk.push<T>(B->deref<T>());

// If a global variable is uninitialized, that means the initializer we've
// compiled for it wasn't a constant expression. Diagnose that.
if (!CheckGlobalInitialized(S, OpPC, Ptr))
return false;

S.Stk.push<T>(Ptr.deref<T>());
return true;
}

Expand All @@ -1029,7 +1036,9 @@ bool SetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {

template <PrimType Name, class T = typename PrimConv<Name>::T>
bool InitGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
S.P.getGlobal(I)->deref<T>() = S.Stk.pop<T>();
const Pointer &P = S.P.getGlobal(I);
P.deref<T>() = S.Stk.pop<T>();
P.initialize();
return true;
}

Expand All @@ -1045,7 +1054,10 @@ bool InitGlobalTemp(InterpState &S, CodePtr OpPC, uint32_t I,
APValue *Cached = Temp->getOrCreateValue(true);
*Cached = APV;

S.P.getGlobal(I)->deref<T>() = S.Stk.pop<T>();
const Pointer &P = S.P.getGlobal(I);
P.deref<T>() = S.Stk.pop<T>();
P.initialize();

return true;
}

Expand Down Expand Up @@ -1267,6 +1279,12 @@ inline bool InitPtrPop(InterpState &S, CodePtr OpPC) {
return true;
}

inline bool InitPtr(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.peek<Pointer>();
Ptr.initialize();
return true;
}

inline bool VirtBaseHelper(InterpState &S, CodePtr OpPC, const RecordDecl *Decl,
const Pointer &Ptr) {
Pointer Base = Ptr;
Expand Down Expand Up @@ -1323,7 +1341,7 @@ bool Store(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.peek<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
if (!Ptr.isRoot())
if (Ptr.canBeInitialized())
Ptr.initialize();
Ptr.deref<T>() = Value;
return true;
Expand All @@ -1335,7 +1353,7 @@ bool StorePop(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
if (!Ptr.isRoot())
if (Ptr.canBeInitialized())
Ptr.initialize();
Ptr.deref<T>() = Value;
return true;
Expand All @@ -1347,7 +1365,7 @@ bool StoreBitField(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.peek<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
if (!Ptr.isRoot())
if (Ptr.canBeInitialized())
Ptr.initialize();
if (const auto *FD = Ptr.getField())
Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx()));
Expand All @@ -1362,7 +1380,7 @@ bool StoreBitFieldPop(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
if (!Ptr.isRoot())
if (Ptr.canBeInitialized())
Ptr.initialize();
if (const auto *FD = Ptr.getField())
Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx()));
Expand Down Expand Up @@ -2041,6 +2059,22 @@ template <> inline Floating ReadArg<Floating>(InterpState &S, CodePtr &OpPC) {
return F;
}

template <>
inline IntegralAP<false> ReadArg<IntegralAP<false>>(InterpState &S,
CodePtr &OpPC) {
IntegralAP<false> I = IntegralAP<false>::deserialize(*OpPC);
OpPC += align(I.bytesToSerialize());
return I;
}

template <>
inline IntegralAP<true> ReadArg<IntegralAP<true>>(InterpState &S,
CodePtr &OpPC) {
IntegralAP<true> I = IntegralAP<true>::deserialize(*OpPC);
OpPC += align(I.bytesToSerialize());
return I;
}

} // namespace interp
} // namespace clang

Expand Down
22 changes: 21 additions & 1 deletion clang/lib/AST/Interp/InterpBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -634,12 +634,23 @@ static bool interp__builtin_addressof(InterpState &S, CodePtr OpPC,
return true;
}

static bool interp__builtin_move(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame, const Function *Func,
const CallExpr *Call) {

PrimType ArgT = S.getContext().classify(Call->getArg(0)).value_or(PT_Ptr);

TYPE_SWITCH(ArgT, const T &Arg = S.Stk.peek<T>(); S.Stk.push<T>(Arg););

return Func->getDecl()->isConstexpr();
}

bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
const CallExpr *Call) {
InterpFrame *Frame = S.Current;
APValue Dummy;

std::optional<PrimType> ReturnT = S.getContext().classify(Call->getType());
std::optional<PrimType> ReturnT = S.getContext().classify(Call);

// If classify failed, we assume void.
assert(ReturnT || Call->getType()->isVoidType());
Expand Down Expand Up @@ -848,6 +859,15 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
return false;
break;

case Builtin::BIas_const:
case Builtin::BIforward:
case Builtin::BIforward_like:
case Builtin::BImove:
case Builtin::BImove_if_noexcept:
if (!interp__builtin_move(S, OpPC, Frame, F, Call))
return false;
break;

default:
return false;
}
Expand Down
11 changes: 2 additions & 9 deletions clang/lib/AST/Interp/InterpFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,7 @@ InterpFrame::InterpFrame(InterpState &S, const Function *Func,
for (auto &Local : Scope.locals()) {
Block *B = new (localBlock(Local.Offset)) Block(Local.Desc);
B->invokeCtor();
InlineDescriptor *ID = localInlineDesc(Local.Offset);
ID->Desc = Local.Desc;
ID->IsActive = true;
ID->Offset = sizeof(InlineDescriptor);
ID->IsBase = false;
ID->IsFieldMutable = false;
ID->IsConst = false;
ID->IsInitialized = false;
new (localInlineDesc(Local.Offset)) InlineDescriptor(Local.Desc);
}
}
}
Expand Down Expand Up @@ -201,7 +194,7 @@ const FunctionDecl *InterpFrame::getCallee() const {

Pointer InterpFrame::getLocalPointer(unsigned Offset) const {
assert(Offset < Func->getFrameSize() && "Invalid local offset.");
return Pointer(localBlock(Offset), sizeof(InlineDescriptor));
return Pointer(localBlock(Offset));
}

Pointer InterpFrame::getParamPointer(unsigned Off) {
Expand Down
9 changes: 6 additions & 3 deletions clang/lib/AST/Interp/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ def ArgUint32 : ArgType { let Name = "uint32_t"; }
def ArgSint64 : ArgType { let Name = "int64_t"; }
def ArgUint64 : ArgType { let Name = "uint64_t"; }
def ArgFloat : ArgType { let Name = "Floating"; }
def ArgIntAP : ArgType { let Name = "IntegralAP<false>"; }
def ArgIntAPS : ArgType { let Name = "IntegralAP<true>"; }
def ArgBool : ArgType { let Name = "bool"; }

def ArgFunction : ArgType { let Name = "const Function *"; }
Expand Down Expand Up @@ -244,6 +246,8 @@ def ConstUint32 : ConstOpcode<Uint32, ArgUint32>;
def ConstSint64 : ConstOpcode<Sint64, ArgSint64>;
def ConstUint64 : ConstOpcode<Uint64, ArgUint64>;
def ConstFloat : ConstOpcode<Float, ArgFloat>;
def constIntAP : ConstOpcode<IntAP, ArgIntAP>;
def constIntAPS : ConstOpcode<IntAPS, ArgIntAPS>;
def ConstBool : ConstOpcode<Bool, ArgBool>;

// [] -> [Integer]
Expand Down Expand Up @@ -317,9 +321,8 @@ def GetPtrBasePop : Opcode {
let Args = [ArgUint32];
}

def InitPtrPop : Opcode {
let Args = [];
}
def InitPtrPop : Opcode;
def InitPtr : Opcode;

def GetPtrDerivedPop : Opcode {
let Args = [ArgUint32];
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/AST/Interp/Pointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
using namespace clang;
using namespace clang::interp;

Pointer::Pointer(Block *Pointee) : Pointer(Pointee, 0, 0) {}
Pointer::Pointer(Block *Pointee)
: Pointer(Pointee, Pointee->getDescriptor()->getMetadataSize(),
Pointee->getDescriptor()->getMetadataSize()) {}

Pointer::Pointer(Block *Pointee, unsigned BaseAndOffset)
: Pointer(Pointee, BaseAndOffset, BaseAndOffset) {}
Expand Down
38 changes: 28 additions & 10 deletions clang/lib/AST/Interp/Pointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,8 @@ class Pointer {

// Pointer to an array of base types - enter block.
if (Base == RootPtrMark)
return Pointer(Pointee, 0, Offset == 0 ? Offset : PastEndMark);
return Pointer(Pointee, sizeof(InlineDescriptor),
Offset == 0 ? Offset : PastEndMark);

// Pointer is one past end - magic offset marks that.
if (isOnePastEnd())
Expand Down Expand Up @@ -179,7 +180,7 @@ class Pointer {
return *this;

// If at base, point to an array of base types.
if (Base == 0)
if (Base == 0 || Base == sizeof(InlineDescriptor))
return Pointer(Pointee, RootPtrMark, 0);

// Step into the containing array, if inside one.
Expand All @@ -196,7 +197,10 @@ class Pointer {
/// Checks if the pointer is live.
bool isLive() const { return Pointee && !Pointee->IsDead; }
/// Checks if the item is a field in an object.
bool isField() const { return Base != 0 && Base != RootPtrMark; }
bool isField() const {
return Base != 0 && Base != sizeof(InlineDescriptor) &&
Base != RootPtrMark && getFieldDesc()->asDecl();
}

/// Accessor for information about the declaration site.
const Descriptor *getDeclDesc() const {
Expand Down Expand Up @@ -227,15 +231,21 @@ class Pointer {

/// Accessors for information about the innermost field.
const Descriptor *getFieldDesc() const {
if (Base == 0 || Base == RootPtrMark)
if (Base == 0 || Base == sizeof(InlineDescriptor) || Base == RootPtrMark)
return getDeclDesc();
return getInlineDesc()->Desc;
}

/// Returns the type of the innermost field.
QualType getType() const {
if (inPrimitiveArray() && Offset != Base)
return getFieldDesc()->getType()->getAsArrayTypeUnsafe()->getElementType();
if (inPrimitiveArray() && Offset != Base) {
// Unfortunately, complex types are not array types in clang, but they are
// for us.
if (const auto *AT = getFieldDesc()->getType()->getAsArrayTypeUnsafe())
return AT->getElementType();
if (const auto *CT = getFieldDesc()->getType()->getAs<ComplexType>())
return CT->getElementType();
}
return getFieldDesc()->getType();
}

Expand Down Expand Up @@ -284,6 +294,8 @@ class Pointer {
bool isRoot() const {
return (Base == 0 || Base == RootPtrMark) && Offset == 0;
}
/// If this pointer has an InlineDescriptor we can use to initialize.
bool canBeInitialized() const { return Pointee && Base > 0; }

/// Returns the record descriptor of a class.
const Record *getRecord() const { return getFieldDesc()->ElemRecord; }
Expand Down Expand Up @@ -315,20 +327,26 @@ class Pointer {

/// Checks if the field is mutable.
bool isMutable() const {
return Base != 0 && getInlineDesc()->IsFieldMutable;
return Base != 0 && Base != sizeof(InlineDescriptor) &&
getInlineDesc()->IsFieldMutable;
}
/// Checks if an object was initialized.
bool isInitialized() const;
/// Checks if the object is active.
bool isActive() const { return Base == 0 || getInlineDesc()->IsActive; }
bool isActive() const {
return Base == 0 || Base == sizeof(InlineDescriptor) ||
getInlineDesc()->IsActive;
}
/// Checks if a structure is a base class.
bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; }
/// Checks if the pointer pointers to a dummy value.
bool isDummy() const { return getDeclDesc()->isDummy(); }

/// Checks if an object or a subfield is mutable.
bool isConst() const {
return Base == 0 ? getDeclDesc()->IsConst : getInlineDesc()->IsConst;
return (Base == 0 || Base == sizeof(InlineDescriptor))
? getDeclDesc()->IsConst
: getInlineDesc()->IsConst;
}

/// Returns the declaration ID.
Expand All @@ -353,7 +371,7 @@ class Pointer {
return 1;

// narrow()ed element in a composite array.
if (Base > 0 && Base == Offset)
if (Base > sizeof(InlineDescriptor) && Base == Offset)
return 0;

if (auto ElemSize = elemSize())
Expand Down
36 changes: 22 additions & 14 deletions clang/lib/AST/Interp/Program.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ unsigned Program::createGlobalString(const StringLiteral *S) {
}

// Create a descriptor for the string.
Descriptor *Desc =
allocateDescriptor(S, CharType, std::nullopt, S->getLength() + 1,
/*isConst=*/true,
/*isTemporary=*/false,
/*isMutable=*/false);
Descriptor *Desc = allocateDescriptor(S, CharType, Descriptor::InlineDescMD,
S->getLength() + 1,
/*isConst=*/true,
/*isTemporary=*/false,
/*isMutable=*/false);

// Allocate storage for the string.
// The byte length does not include the null terminator.
Expand All @@ -67,6 +67,8 @@ unsigned Program::createGlobalString(const StringLiteral *S) {
auto *G = new (Allocator, Sz) Global(Desc, /*isStatic=*/true,
/*isExtern=*/false);
G->block()->invokeCtor();

new (G->block()->rawData()) InlineDescriptor(Desc);
Globals.push_back(G);

// Construct the string in storage.
Expand All @@ -78,16 +80,19 @@ unsigned Program::createGlobalString(const StringLiteral *S) {
case PT_Sint8: {
using T = PrimConv<PT_Sint8>::T;
Field.deref<T>() = T::from(CodePoint, BitWidth);
Field.initialize();
break;
}
case PT_Uint16: {
using T = PrimConv<PT_Uint16>::T;
Field.deref<T>() = T::from(CodePoint, BitWidth);
Field.initialize();
break;
}
case PT_Uint32: {
using T = PrimConv<PT_Uint32>::T;
Field.deref<T>() = T::from(CodePoint, BitWidth);
Field.initialize();
break;
}
default:
Expand Down Expand Up @@ -190,12 +195,13 @@ std::optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty,
Descriptor *Desc;
const bool IsConst = Ty.isConstQualified();
const bool IsTemporary = D.dyn_cast<const Expr *>();
if (auto T = Ctx.classify(Ty)) {
Desc = createDescriptor(D, *T, std::nullopt, IsConst, IsTemporary);
} else {
Desc = createDescriptor(D, Ty.getTypePtr(), std::nullopt, IsConst,
IsTemporary);
}
if (std::optional<PrimType> T = Ctx.classify(Ty))
Desc =
createDescriptor(D, *T, Descriptor::InlineDescMD, IsConst, IsTemporary);
else
Desc = createDescriptor(D, Ty.getTypePtr(), Descriptor::InlineDescMD,
IsConst, IsTemporary);

if (!Desc)
return std::nullopt;

Expand All @@ -206,6 +212,8 @@ std::optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty,
Global(getCurrentDecl(), Desc, IsStatic, IsExtern);
G->block()->invokeCtor();

// Initialize InlineDescriptor fields.
new (G->block()->rawData()) InlineDescriptor(Desc);
Globals.push_back(G);

return I;
Expand Down Expand Up @@ -339,7 +347,7 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
// Arrays of composites. In this case, the array is a list of pointers,
// followed by the actual elements.
const Descriptor *ElemDesc = createDescriptor(
D, ElemTy.getTypePtr(), std::nullopt, IsConst, IsTemporary);
D, ElemTy.getTypePtr(), MDSize, IsConst, IsTemporary);
if (!ElemDesc)
return nullptr;
unsigned ElemSize =
Expand All @@ -355,14 +363,14 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
// is forbidden on pointers to such objects.
if (isa<IncompleteArrayType>(ArrayType)) {
if (std::optional<PrimType> T = Ctx.classify(ElemTy)) {
return allocateDescriptor(D, *T, IsTemporary,
return allocateDescriptor(D, *T, MDSize, IsTemporary,
Descriptor::UnknownSize{});
} else {
const Descriptor *Desc = createDescriptor(D, ElemTy.getTypePtr(),
MDSize, IsConst, IsTemporary);
if (!Desc)
return nullptr;
return allocateDescriptor(D, Desc, IsTemporary,
return allocateDescriptor(D, Desc, MDSize, IsTemporary,
Descriptor::UnknownSize{});
}
}
Expand Down
33 changes: 32 additions & 1 deletion clang/lib/AST/JSONNodeDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ void JSONNodeDumper::Visit(const Type *T) {
return;

JOS.attribute("kind", (llvm::Twine(T->getTypeClassName()) + "Type").str());
JOS.attribute("type", createQualType(QualType(T, 0), /*Desugar*/ false));
JOS.attribute("type", createQualType(QualType(T, 0), /*Desugar=*/false));
attributeOnlyIfTrue("containsErrors", T->containsErrors());
attributeOnlyIfTrue("isDependent", T->isDependentType());
attributeOnlyIfTrue("isInstantiationDependent",
Expand All @@ -96,6 +96,21 @@ void JSONNodeDumper::Visit(QualType T) {
JOS.attribute("qualifiers", T.split().Quals.getAsString());
}

void JSONNodeDumper::Visit(TypeLoc TL) {
if (TL.isNull())
return;
JOS.attribute("kind",
(llvm::Twine(TL.getTypeLocClass() == TypeLoc::Qualified
? "Qualified"
: TL.getTypePtr()->getTypeClassName()) +
"TypeLoc")
.str());
JOS.attribute("type",
createQualType(QualType(TL.getType()), /*Desugar=*/false));
JOS.attributeObject("range",
[TL, this] { writeSourceRange(TL.getSourceRange()); });
}

void JSONNodeDumper::Visit(const Decl *D) {
JOS.attribute("id", createPointerRepresentation(D));

Expand Down Expand Up @@ -223,6 +238,22 @@ void JSONNodeDumper::Visit(const APValue &Value, QualType Ty) {
JOS.attribute("value", OS.str());
}

void JSONNodeDumper::Visit(const ConceptReference *CR) {
JOS.attribute("kind", "ConceptReference");
JOS.attribute("id", createPointerRepresentation(CR->getNamedConcept()));
if (const auto *Args = CR->getTemplateArgsAsWritten()) {
JOS.attributeArray("templateArgsAsWritten", [Args, this] {
for (const TemplateArgumentLoc &TAL : Args->arguments())
JOS.object(
[&TAL, this] { Visit(TAL.getArgument(), TAL.getSourceRange()); });
});
}
JOS.attributeObject("loc",
[CR, this] { writeSourceLocation(CR->getLocation()); });
JOS.attributeObject("range",
[CR, this] { writeSourceRange(CR->getSourceRange()); });
}

void JSONNodeDumper::writeIncludeStack(PresumedLoc Loc, bool JustFirst) {
if (Loc.isInvalid())
return;
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/OpenMPClause.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1957,6 +1957,8 @@ void OMPClausePrinter::VisitOMPRelaxedClause(OMPRelaxedClause *) {
OS << "relaxed";
}

void OMPClausePrinter::VisitOMPWeakClause(OMPWeakClause *) { OS << "weak"; }

void OMPClausePrinter::VisitOMPThreadsClause(OMPThreadsClause *) {
OS << "threads";
}
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/StmtProfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,8 @@ void OMPClauseProfiler::VisitOMPReleaseClause(const OMPReleaseClause *) {}

void OMPClauseProfiler::VisitOMPRelaxedClause(const OMPRelaxedClause *) {}

void OMPClauseProfiler::VisitOMPWeakClause(const OMPWeakClause *) {}

void OMPClauseProfiler::VisitOMPThreadsClause(const OMPThreadsClause *) {}

void OMPClauseProfiler::VisitOMPSIMDClause(const OMPSIMDClause *) {}
Expand Down
34 changes: 30 additions & 4 deletions clang/lib/AST/TextNodeDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "clang/AST/LocInfoType.h"
#include "clang/AST/NestedNameSpecifier.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLocVisitor.h"
#include "clang/Basic/Module.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Specifiers.h"
Expand Down Expand Up @@ -240,6 +241,27 @@ void TextNodeDumper::Visit(QualType T) {
OS << " " << T.split().Quals.getAsString();
}

void TextNodeDumper::Visit(TypeLoc TL) {
if (!TL) {
ColorScope Color(OS, ShowColors, NullColor);
OS << "<<<NULL>>>";
return;
}

{
ColorScope Color(OS, ShowColors, TypeColor);
OS << (TL.getTypeLocClass() == TypeLoc::Qualified
? "Qualified"
: TL.getType()->getTypeClassName())
<< "TypeLoc";
}
dumpSourceRange(TL.getSourceRange());
OS << ' ';
dumpBareType(TL.getType(), /*Desugar=*/false);

TypeLocVisitor<TextNodeDumper>::Visit(TL);
}

void TextNodeDumper::Visit(const Decl *D) {
if (!D) {
ColorScope Color(OS, ShowColors, NullColor);
Expand Down Expand Up @@ -1801,11 +1823,8 @@ void TextNodeDumper::VisitAutoType(const AutoType *T) {
OS << " decltype(auto)";
if (!T->isDeduced())
OS << " undeduced";
if (T->isConstrained()) {
if (T->isConstrained())
dumpDeclRef(T->getTypeConstraintConcept());
for (const auto &Arg : T->getTypeConstraintArguments())
VisitTemplateArgument(Arg);
}
}

void TextNodeDumper::VisitDeducedTemplateSpecializationType(
Expand Down Expand Up @@ -1838,6 +1857,13 @@ void TextNodeDumper::VisitPackExpansionType(const PackExpansionType *T) {
OS << " expansions " << *N;
}

void TextNodeDumper::VisitTypeLoc(TypeLoc TL) {
// By default, add extra Type details with no extra loc info.
TypeVisitor<TextNodeDumper>::Visit(TL.getTypePtr());
}
// FIXME: override behavior for TypeLocs that have interesting location
// information, such as the qualifier in ElaboratedTypeLoc.

void TextNodeDumper::VisitLabelDecl(const LabelDecl *D) { dumpName(D); }

void TextNodeDumper::VisitTypedefDecl(const TypedefDecl *D) {
Expand Down
36 changes: 30 additions & 6 deletions clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1009,12 +1009,15 @@ bool Environment::allows(const Formula &F) const {
}

void Environment::dump(raw_ostream &OS) const {
// FIXME: add printing for remaining fields and allow caller to decide what
// fields are printed.
OS << "DeclToLoc:\n";
for (auto [D, L] : DeclToLoc)
OS << " [" << D->getNameAsString() << ", " << L << "]\n";
llvm::DenseMap<const StorageLocation *, std::string> LocToName;
if (ThisPointeeLoc != nullptr)
LocToName[ThisPointeeLoc] = "this";

OS << "DeclToLoc:\n";
for (auto [D, L] : DeclToLoc) {
auto Iter = LocToName.insert({L, D->getNameAsString()}).first;
OS << " [" << Iter->second << ", " << L << "]\n";
}
OS << "ExprToLoc:\n";
for (auto [E, L] : ExprToLoc)
OS << " [" << E << ", " << L << "]\n";
Expand All @@ -1025,7 +1028,28 @@ void Environment::dump(raw_ostream &OS) const {

OS << "LocToVal:\n";
for (auto [L, V] : LocToVal) {
OS << " [" << L << ", " << V << ": " << *V << "]\n";
OS << " [" << L;
if (auto Iter = LocToName.find(L); Iter != LocToName.end())
OS << " (" << Iter->second << ")";
OS << ", " << V << ": " << *V << "]\n";
}

if (const FunctionDecl *Func = getCurrentFunc()) {
if (Func->getReturnType()->isReferenceType()) {
OS << "ReturnLoc: " << ReturnLoc;
if (auto Iter = LocToName.find(ReturnLoc); Iter != LocToName.end())
OS << " (" << Iter->second << ")";
OS << "\n";
} else if (!Func->getReturnType()->isVoidType()) {
if (ReturnVal == nullptr)
OS << "ReturnVal: nullptr\n";
else
OS << "ReturnVal: " << *ReturnVal << "\n";
}

if (isa<CXXMethodDecl>(Func)) {
OS << "ThisPointeeLoc: " << ThisPointeeLoc << "\n";
}
}

OS << "\n";
Expand Down
102 changes: 60 additions & 42 deletions clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,13 +158,17 @@ class HTMLLogger : public Logger {

StreamFactory Streams;
std::unique_ptr<llvm::raw_ostream> OS;
std::optional<llvm::json::OStream> JOS;
std::string JSON;
llvm::raw_string_ostream JStringStream{JSON};
llvm::json::OStream JOS{JStringStream, /*Indent=*/2};

const ControlFlowContext *CFG;
// Timeline of iterations of CFG block visitation.
std::vector<Iteration> Iters;
// Indexes in `Iters` of the iterations for each block.
llvm::DenseMap<const CFGBlock *, llvm::SmallVector<size_t>> BlockIters;
// For a given block ID, did the block converge (on the last iteration)?
llvm::BitVector BlockConverged;
// The messages logged in the current context but not yet written.
std::string ContextLogs;
// The number of elements we have visited within the current CFG block.
Expand All @@ -178,6 +182,8 @@ class HTMLLogger : public Logger {
this->CFG = &CFG;
*OS << llvm::StringRef(HTMLLogger_html).split("<?INJECT?>").first;

BlockConverged.resize(CFG.getCFG().getNumBlockIDs());

const auto &D = CFG.getDecl();
const auto &SM = A.getASTContext().getSourceManager();
*OS << "<title>";
Expand All @@ -191,37 +197,37 @@ class HTMLLogger : public Logger {
*OS << "<script>" << HTMLLogger_js << "</script>\n";

writeCode();
writeCFG();

*OS << "<script>var HTMLLoggerData = \n";
JOS.emplace(*OS, /*Indent=*/2);
JOS->objectBegin();
JOS->attributeBegin("states");
JOS->objectBegin();
JOS.objectBegin();
JOS.attributeBegin("states");
JOS.objectBegin();
}
// Between beginAnalysis() and endAnalysis() we write all the states for
// particular analysis points into the `timeline` array.
void endAnalysis() override {
JOS->objectEnd();
JOS->attributeEnd();
JOS.objectEnd();
JOS.attributeEnd();

JOS->attributeArray("timeline", [&] {
JOS.attributeArray("timeline", [&] {
for (const auto &E : Iters) {
JOS->object([&] {
JOS->attribute("block", blockID(E.Block->getBlockID()));
JOS->attribute("iter", E.Iter);
JOS->attribute("post_visit", E.PostVisit);
JOS->attribute("converged", E.Converged);
JOS.object([&] {
JOS.attribute("block", blockID(E.Block->getBlockID()));
JOS.attribute("iter", E.Iter);
JOS.attribute("post_visit", E.PostVisit);
JOS.attribute("converged", E.Converged);
});
}
});
JOS->attributeObject("cfg", [&] {
JOS.attributeObject("cfg", [&] {
for (const auto &E : BlockIters)
writeBlock(*E.first, E.second);
});

JOS->objectEnd();
JOS.reset();
JOS.objectEnd();

writeCFG();

*OS << "<script>var HTMLLoggerData = \n";
*OS << JSON;
*OS << ";\n</script>\n";
*OS << llvm::StringRef(HTMLLogger_html).split("<?INJECT?>").second;
}
Expand All @@ -231,6 +237,8 @@ class HTMLLogger : public Logger {
unsigned IterNum = BIter.size() + 1;
BIter.push_back(Iters.size());
Iters.push_back({&B, IterNum, PostVisit, /*Converged=*/false});
if (!PostVisit)
BlockConverged[B.getBlockID()] = false;
ElementIndex = 0;
}
void enterElement(const CFGElement &E) override {
Expand Down Expand Up @@ -261,11 +269,11 @@ class HTMLLogger : public Logger {
unsigned Block = Iters.back().Block->getBlockID();
unsigned Iter = Iters.back().Iter;
bool PostVisit = Iters.back().PostVisit;
JOS->attributeObject(elementIterID(Block, Iter, ElementIndex), [&] {
JOS->attribute("block", blockID(Block));
JOS->attribute("iter", Iter);
JOS->attribute("post_visit", PostVisit);
JOS->attribute("element", ElementIndex);
JOS.attributeObject(elementIterID(Block, Iter, ElementIndex), [&] {
JOS.attribute("block", blockID(Block));
JOS.attribute("iter", Iter);
JOS.attribute("post_visit", PostVisit);
JOS.attribute("element", ElementIndex);

// If this state immediately follows an Expr, show its built-in model.
if (ElementIndex > 0) {
Expand All @@ -274,28 +282,31 @@ class HTMLLogger : public Logger {
if (const Expr *E = S ? llvm::dyn_cast<Expr>(S->getStmt()) : nullptr) {
if (E->isPRValue()) {
if (auto *V = State.Env.getValue(*E))
JOS->attributeObject(
"value", [&] { ModelDumper(*JOS, State.Env).dump(*V); });
JOS.attributeObject(
"value", [&] { ModelDumper(JOS, State.Env).dump(*V); });
} else {
if (auto *Loc = State.Env.getStorageLocation(*E))
JOS->attributeObject(
"value", [&] { ModelDumper(*JOS, State.Env).dump(*Loc); });
JOS.attributeObject(
"value", [&] { ModelDumper(JOS, State.Env).dump(*Loc); });
}
}
}
if (!ContextLogs.empty()) {
JOS->attribute("logs", ContextLogs);
JOS.attribute("logs", ContextLogs);
ContextLogs.clear();
}
{
std::string BuiltinLattice;
llvm::raw_string_ostream BuiltinLatticeS(BuiltinLattice);
State.Env.dump(BuiltinLatticeS);
JOS->attribute("builtinLattice", BuiltinLattice);
JOS.attribute("builtinLattice", BuiltinLattice);
}
});
}
void blockConverged() override { Iters.back().Converged = true; }
void blockConverged() override {
Iters.back().Converged = true;
BlockConverged[Iters.back().Block->getBlockID()] = true;
}

void logText(llvm::StringRef S) override {
ContextLogs.append(S.begin(), S.end());
Expand All @@ -307,23 +318,23 @@ class HTMLLogger : public Logger {
// Currently this is just the list of elements in execution order.
// FIXME: an AST dump would be a useful view, too.
void writeBlock(const CFGBlock &B, llvm::ArrayRef<size_t> ItersForB) {
JOS->attributeObject(blockID(B.getBlockID()), [&] {
JOS->attributeArray("iters", [&] {
JOS.attributeObject(blockID(B.getBlockID()), [&] {
JOS.attributeArray("iters", [&] {
for (size_t IterIdx : ItersForB) {
const Iteration &Iter = Iters[IterIdx];
JOS->object([&] {
JOS->attribute("iter", Iter.Iter);
JOS->attribute("post_visit", Iter.PostVisit);
JOS->attribute("converged", Iter.Converged);
JOS.object([&] {
JOS.attribute("iter", Iter.Iter);
JOS.attribute("post_visit", Iter.PostVisit);
JOS.attribute("converged", Iter.Converged);
});
}
});
JOS->attributeArray("elements", [&] {
JOS.attributeArray("elements", [&] {
for (const auto &Elt : B.Elements) {
std::string Dump;
llvm::raw_string_ostream DumpS(Dump);
Elt.dumpToStream(DumpS);
JOS->value(Dump);
JOS.value(Dump);
}
});
});
Expand Down Expand Up @@ -477,7 +488,7 @@ class HTMLLogger : public Logger {
}

// Produce a graphviz description of a CFG.
static std::string buildCFGDot(const clang::CFG &CFG) {
std::string buildCFGDot(const clang::CFG &CFG) {
std::string Graph;
llvm::raw_string_ostream GraphS(Graph);
// Graphviz likes to add unhelpful tooltips everywhere, " " suppresses.
Expand All @@ -486,8 +497,15 @@ class HTMLLogger : public Logger {
node[class=bb, shape=square, fontname="sans-serif", tooltip=" "]
edge[tooltip = " "]
)";
for (unsigned I = 0; I < CFG.getNumBlockIDs(); ++I)
GraphS << " " << blockID(I) << " [id=" << blockID(I) << "]\n";
for (unsigned I = 0; I < CFG.getNumBlockIDs(); ++I) {
std::string Name = blockID(I);
// Rightwards arrow, vertical line
char ConvergenceMarker[] = u8"\\n\u2192\u007c";
if (BlockConverged[I])
Name += ConvergenceMarker;
GraphS << " " << blockID(I) << " [id=" << blockID(I) << " label=\""
<< Name << "\"]\n";
}
for (const auto *Block : CFG) {
for (const auto &Succ : Block->succs()) {
if (Succ.getReachableBlock())
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Analysis/FlowSensitive/Transfer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,13 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {

copyRecord(*LocSrc, *LocDst, Env);
Env.setStorageLocation(*S, *LocDst);
return;
}

// CXXOperatorCallExpr can be prvalues. Call `VisitCallExpr`() to create
// a `RecordValue` for them so that `Environment::getResultObjectLocation()`
// can return a value.
VisitCallExpr(S);
}

void VisitCXXFunctionalCastExpr(const CXXFunctionalCastExpr *S) {
Expand Down
9 changes: 7 additions & 2 deletions clang/lib/Basic/Targets/NVPTX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ NVPTXTargetInfo::NVPTXTargetInfo(const llvm::Triple &Triple,
// Define available target features
// These must be defined in sorted order!
NoAsmVariants = true;
GPU = CudaArch::SM_20;
GPU = CudaArch::UNUSED;

if (TargetPointerWidth == 32)
resetDataLayout("e-p:32:32-i64:64-i128:128-v16:16-v32:32-n16:32:64");
Expand Down Expand Up @@ -169,6 +169,11 @@ void NVPTXTargetInfo::getTargetDefines(const LangOptions &Opts,
MacroBuilder &Builder) const {
Builder.defineMacro("__PTX__");
Builder.defineMacro("__NVPTX__");

// Skip setting architecture dependent macros if undefined.
if (GPU == CudaArch::UNUSED && !HostTarget)
return;

if (Opts.CUDAIsDevice || Opts.OpenMPIsTargetDevice || !HostTarget) {
// Set __CUDA_ARCH__ for the GPU specified.
std::string CUDAArchCode = [this] {
Expand Down Expand Up @@ -220,10 +225,10 @@ void NVPTXTargetInfo::getTargetDefines(const LangOptions &Opts,
case CudaArch::Generic:
case CudaArch::LAST:
break;
case CudaArch::UNUSED:
case CudaArch::UNKNOWN:
assert(false && "No GPU arch when compiling CUDA device code.");
return "";
case CudaArch::UNUSED:
case CudaArch::SM_20:
return "200";
case CudaArch::SM_21:
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Basic/Targets/NVPTX.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ class LLVM_LIBRARY_VISIBILITY NVPTXTargetInfo : public TargetInfo {
initFeatureMap(llvm::StringMap<bool> &Features, DiagnosticsEngine &Diags,
StringRef CPU,
const std::vector<std::string> &FeaturesVec) const override {
Features[CudaArchToString(GPU)] = true;
if (GPU != CudaArch::UNUSED)
Features[CudaArchToString(GPU)] = true;
Features["ptx" + std::to_string(PTXVersion)] = true;
return TargetInfo::initFeatureMap(Features, Diags, CPU, FeaturesVec);
}
Expand Down
17 changes: 15 additions & 2 deletions clang/lib/CodeGen/CGExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5848,6 +5848,7 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, const CGCallee &OrigCallee
// destruction order is not necessarily reverse construction order.
// FIXME: Revisit this based on C++ committee response to unimplementability.
EvaluationOrder Order = EvaluationOrder::Default;
bool StaticOperator = false;
if (auto *OCE = dyn_cast<CXXOperatorCallExpr>(E)) {
if (OCE->isAssignmentOp())
Order = EvaluationOrder::ForceRightToLeft;
Expand All @@ -5865,10 +5866,22 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, const CGCallee &OrigCallee
break;
}
}

if (const auto *MD =
dyn_cast_if_present<CXXMethodDecl>(OCE->getCalleeDecl());
MD && MD->isStatic())
StaticOperator = true;
}

EmitCallArgs(Args, dyn_cast<FunctionProtoType>(FnType), E->arguments(),
E->getDirectCallee(), /*ParamsToSkip*/ 0, Order);
auto Arguments = E->arguments();
if (StaticOperator) {
// If we're calling a static operator, we need to emit the object argument
// and ignore it.
EmitIgnoredExpr(E->getArg(0));
Arguments = drop_begin(Arguments, 1);
}
EmitCallArgs(Args, dyn_cast<FunctionProtoType>(FnType), Arguments,
E->getDirectCallee(), /*ParamsToSkip=*/0, Order);

const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeFreeFunctionCall(
Args, FnType, /*ChainCall=*/Chain);
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CodeGen/CGStmtOpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6546,6 +6546,9 @@ void CodeGenFunction::EmitOMPAtomicDirective(const OMPAtomicDirective &S) {
// Find first clause (skip seq_cst|acq_rel|aqcuire|release|relaxed clause,
// if it is first).
OpenMPClauseKind K = C->getClauseKind();
// TBD
if (K == OMPC_weak)
return;
if (K == OMPC_seq_cst || K == OMPC_acq_rel || K == OMPC_acquire ||
K == OMPC_release || K == OMPC_relaxed || K == OMPC_hint)
continue;
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2705,7 +2705,7 @@ static StringRef EnumComplexRangeToStr(LangOptions::ComplexRangeKind Range) {
static void EmitComplexRangeDiag(const Driver &D,
LangOptions::ComplexRangeKind Range1,
LangOptions::ComplexRangeKind Range2) {
if (Range1 != LangOptions::ComplexRangeKind::CX_Full)
if (Range1 != Range2 && Range1 != LangOptions::ComplexRangeKind::CX_None)
D.Diag(clang::diag::warn_drv_overriding_option)
<< EnumComplexRangeToStr(Range1) << EnumComplexRangeToStr(Range2);
}
Expand Down
20 changes: 14 additions & 6 deletions clang/lib/Driver/ToolChains/Cuda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,11 @@ void NVPTX::Assembler::ConstructJob(Compilation &C, const JobAction &JA,
GPUArchName = JA.getOffloadingArch();
} else {
GPUArchName = Args.getLastArgValue(options::OPT_march_EQ);
assert(!GPUArchName.empty() && "Must have an architecture passed in.");
if (GPUArchName.empty()) {
C.getDriver().Diag(diag::err_drv_offload_missing_gpu_arch)
<< getToolChain().getArchName() << getShortName();
return;
}
}

// Obtain architecture from the action.
Expand Down Expand Up @@ -593,7 +597,11 @@ void NVPTX::Linker::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back("-v");

StringRef GPUArch = Args.getLastArgValue(options::OPT_march_EQ);
assert(!GPUArch.empty() && "At least one GPU Arch required for nvlink.");
if (GPUArch.empty()) {
C.getDriver().Diag(diag::err_drv_offload_missing_gpu_arch)
<< getToolChain().getArchName() << getShortName();
return;
}

CmdArgs.push_back("-arch");
CmdArgs.push_back(Args.MakeArgString(GPUArch));
Expand Down Expand Up @@ -726,9 +734,8 @@ NVPTXToolChain::NVPTXToolChain(const Driver &D, const llvm::Triple &Triple,
llvm::opt::DerivedArgList *
NVPTXToolChain::TranslateArgs(const llvm::opt::DerivedArgList &Args,
StringRef BoundArch,
Action::OffloadKind DeviceOffloadKind) const {
DerivedArgList *DAL =
ToolChain::TranslateArgs(Args, BoundArch, DeviceOffloadKind);
Action::OffloadKind OffloadKind) const {
DerivedArgList *DAL = ToolChain::TranslateArgs(Args, BoundArch, OffloadKind);
if (!DAL)
DAL = new DerivedArgList(Args.getBaseArgs());

Expand All @@ -738,7 +745,8 @@ NVPTXToolChain::TranslateArgs(const llvm::opt::DerivedArgList &Args,
if (!llvm::is_contained(*DAL, A))
DAL->append(A);

if (!DAL->hasArg(options::OPT_march_EQ)) {
// TODO: We should accept 'generic' as a valid architecture.
if (!DAL->hasArg(options::OPT_march_EQ) && OffloadKind != Action::OFK_None) {
DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_march_EQ),
CudaArchToString(CudaArch::CudaDefault));
} else if (DAL->getLastArgValue(options::OPT_march_EQ) == "native") {
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/Driver/ToolChains/HIPAMD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ void AMDGCN::Linker::constructLldCommand(Compilation &C, const JobAction &JA,
if (IsThinLTO)
LldArgs.push_back(Args.MakeArgString("-plugin-opt=-force-import-all"));

for (const Arg *A : Args.filtered(options::OPT_mllvm)) {
LldArgs.push_back(
Args.MakeArgString(Twine("-plugin-opt=") + A->getValue(0)));
}

if (C.getDriver().isSaveTempsEnabled())
LldArgs.push_back("-save-temps");

Expand Down
5 changes: 4 additions & 1 deletion clang/lib/Driver/ToolChains/MinGW.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,10 @@ void tools::MinGW::Linker::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back("thumb2pe");
break;
case llvm::Triple::aarch64:
CmdArgs.push_back("arm64pe");
if (TC.getEffectiveTriple().isWindowsArm64EC())
CmdArgs.push_back("arm64ecpe");
else
CmdArgs.push_back("arm64pe");
break;
default:
D.Diag(diag::err_target_unknown_triple) << TC.getEffectiveTriple().str();
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Parse/ParseOpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3314,6 +3314,7 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind,
case OMPC_acquire:
case OMPC_release:
case OMPC_relaxed:
case OMPC_weak:
case OMPC_threads:
case OMPC_simd:
case OMPC_nogroup:
Expand Down
5 changes: 2 additions & 3 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7611,9 +7611,8 @@ bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall,
unsigned NumArgs = TheCall->getNumArgs();

Expr *ImplicitThis = nullptr;
if (IsMemberOperatorCall && !FDecl->isStatic() &&
!FDecl->hasCXXExplicitFunctionObjectParameter()) {
// If this is a call to a non-static member operator, hide the first
if (IsMemberOperatorCall && !FDecl->hasCXXExplicitFunctionObjectParameter()) {
// If this is a call to a member operator, hide the first
// argument from checkCall.
// FIXME: Our choice of AST representation here is less than ideal.
ImplicitThis = Args[0];
Expand Down
14 changes: 13 additions & 1 deletion clang/lib/Sema/SemaConcept.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -807,8 +807,20 @@ static const Expr *SubstituteConstraintExpressionWithoutSatisfaction(
ScopeForParameters.InstantiatedLocal(PVD, PVD);

std::optional<Sema::CXXThisScopeRAII> ThisScope;
if (auto *RD = dyn_cast<CXXRecordDecl>(DeclInfo.getDeclContext()))

// See TreeTransform::RebuildTemplateSpecializationType. A context scope is
// essential for having an injected class as the canonical type for a template
// specialization type at the rebuilding stage. This guarantees that, for
// out-of-line definitions, injected class name types and their equivalent
// template specializations can be profiled to the same value, which makes it
// possible that e.g. constraints involving C<Class<T>> and C<Class> are
// perceived identical.
std::optional<Sema::ContextRAII> ContextScope;
if (auto *RD = dyn_cast<CXXRecordDecl>(DeclInfo.getDeclContext())) {
ThisScope.emplace(S, const_cast<CXXRecordDecl *>(RD), Qualifiers());
ContextScope.emplace(S, const_cast<DeclContext *>(cast<DeclContext>(RD)),
/*NewThisContext=*/false);
}
ExprResult SubstConstr = S.SubstConstraintExprWithoutSatisfaction(
const_cast<clang::Expr *>(ConstrExpr), MLTAL);
if (SFINAE.hasErrorOccurred() || !SubstConstr.isUsable())
Expand Down
33 changes: 32 additions & 1 deletion clang/lib/Sema/SemaOpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12708,9 +12708,11 @@ StmtResult Sema::ActOnOpenMPAtomicDirective(ArrayRef<OMPClause *> Clauses,
}
break;
}
case OMPC_weak:
case OMPC_fail: {
if (!EncounteredAtomicKinds.contains(OMPC_compare)) {
Diag(C->getBeginLoc(), diag::err_omp_atomic_fail_no_compare)
Diag(C->getBeginLoc(), diag::err_omp_atomic_no_compare)
<< getOpenMPClauseName(C->getClauseKind())
<< SourceRange(C->getBeginLoc(), C->getEndLoc());
return StmtError();
}
Expand Down Expand Up @@ -13202,6 +13204,27 @@ StmtResult Sema::ActOnOpenMPAtomicDirective(ArrayRef<OMPClause *> Clauses,
E = Checker.getE();
D = Checker.getD();
CE = Checker.getCond();
// The weak clause may only appear if the resulting atomic operation is
// an atomic conditional update for which the comparison tests for
// equality. It was not possible to do this check in
// OpenMPAtomicCompareChecker::checkStmt() as the check for OMPC_weak
// could not be performed (Clauses are not available).
auto *It = find_if(Clauses, [](OMPClause *C) {
return C->getClauseKind() == llvm::omp::Clause::OMPC_weak;
});
if (It != Clauses.end()) {
auto *Cond = dyn_cast<BinaryOperator>(CE);
if (Cond->getOpcode() != BO_EQ) {
ErrorInfo.Error = Checker.ErrorTy::NotAnAssignment;
ErrorInfo.ErrorLoc = Cond->getExprLoc();
ErrorInfo.NoteLoc = Cond->getOperatorLoc();
ErrorInfo.ErrorRange = ErrorInfo.NoteRange = Cond->getSourceRange();

Diag(ErrorInfo.ErrorLoc, diag::err_omp_atomic_weak_no_equality)
<< ErrorInfo.ErrorRange;
return StmtError();
}
}
// We reuse IsXLHSInRHSPart to tell if it is in the form 'x ordop expr'.
IsXLHSInRHSPart = Checker.isXBinopExpr();
}
Expand Down Expand Up @@ -17593,6 +17616,9 @@ OMPClause *Sema::ActOnOpenMPClause(OpenMPClauseKind Kind,
case OMPC_relaxed:
Res = ActOnOpenMPRelaxedClause(StartLoc, EndLoc);
break;
case OMPC_weak:
Res = ActOnOpenMPWeakClause(StartLoc, EndLoc);
break;
case OMPC_threads:
Res = ActOnOpenMPThreadsClause(StartLoc, EndLoc);
break;
Expand Down Expand Up @@ -17781,6 +17807,11 @@ OMPClause *Sema::ActOnOpenMPRelaxedClause(SourceLocation StartLoc,
return new (Context) OMPRelaxedClause(StartLoc, EndLoc);
}

OMPClause *Sema::ActOnOpenMPWeakClause(SourceLocation StartLoc,
SourceLocation EndLoc) {
return new (Context) OMPWeakClause(StartLoc, EndLoc);
}

OMPClause *Sema::ActOnOpenMPThreadsClause(SourceLocation StartLoc,
SourceLocation EndLoc) {
return new (Context) OMPThreadsClause(StartLoc, EndLoc);
Expand Down
44 changes: 18 additions & 26 deletions clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5664,10 +5664,15 @@ static ImplicitConversionSequence TryObjectArgumentInitialization(
assert(FromType->isRecordType());

QualType ClassType = S.Context.getTypeDeclType(ActingContext);
// [class.dtor]p2: A destructor can be invoked for a const, volatile or
// const volatile object.
// C++98 [class.dtor]p2:
// A destructor can be invoked for a const, volatile or const volatile
// object.
// C++98 [over.match.funcs]p4:
// For static member functions, the implicit object parameter is considered
// to match any object (since if the function is selected, the object is
// discarded).
Qualifiers Quals = Method->getMethodQualifiers();
if (isa<CXXDestructorDecl>(Method)) {
if (isa<CXXDestructorDecl>(Method) || Method->isStatic()) {
Quals.addConst();
Quals.addVolatile();
}
Expand Down Expand Up @@ -15061,15 +15066,15 @@ ExprResult Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
CXXMethodDecl *Method = cast<CXXMethodDecl>(FnDecl);
SmallVector<Expr *, 2> MethodArgs;

// Handle 'this' parameter if the selected function is not static.
// Initialize the object parameter.
if (Method->isExplicitObjectMemberFunction()) {
ExprResult Res =
InitializeExplicitObjectArgument(*this, Args[0], Method);
if (Res.isInvalid())
return ExprError();
Args[0] = Res.get();
ArgExpr = Args;
} else if (Method->isInstance()) {
} else {
ExprResult Arg0 = PerformImplicitObjectArgumentInitialization(
Args[0], /*Qualifier=*/nullptr, Best->FoundDecl, Method);
if (Arg0.isInvalid())
Expand Down Expand Up @@ -15097,15 +15102,9 @@ ExprResult Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
ExprValueKind VK = Expr::getValueKindForType(ResultTy);
ResultTy = ResultTy.getNonLValueExprType(Context);

CallExpr *TheCall;
if (Method->isInstance())
TheCall = CXXOperatorCallExpr::Create(
Context, OO_Subscript, FnExpr.get(), MethodArgs, ResultTy, VK,
RLoc, CurFPFeatureOverrides());
else
TheCall =
CallExpr::Create(Context, FnExpr.get(), MethodArgs, ResultTy, VK,
RLoc, CurFPFeatureOverrides());
CallExpr *TheCall = CXXOperatorCallExpr::Create(
Context, OO_Subscript, FnExpr.get(), MethodArgs, ResultTy, VK, RLoc,
CurFPFeatureOverrides());

if (CheckCallReturnType(FnDecl->getReturnType(), LLoc, TheCall, FnDecl))
return ExprError();
Expand Down Expand Up @@ -15733,15 +15732,13 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,

bool IsError = false;

// Initialize the implicit object parameter if needed.
// Since C++23, this could also be a call to a static call operator
// which we emit as a regular CallExpr.
// Initialize the object parameter.
llvm::SmallVector<Expr *, 8> NewArgs;
if (Method->isExplicitObjectMemberFunction()) {
// FIXME: we should do that during the definition of the lambda when we can.
DiagnoseInvalidExplicitObjectParameterInLambda(Method);
PrepareExplicitObjectArgument(*this, Method, Obj, Args, NewArgs);
} else if (Method->isInstance()) {
} else {
ExprResult ObjRes = PerformImplicitObjectArgumentInitialization(
Object.get(), /*Qualifier=*/nullptr, Best->FoundDecl, Method);
if (ObjRes.isInvalid())
Expand Down Expand Up @@ -15775,14 +15772,9 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
ExprValueKind VK = Expr::getValueKindForType(ResultTy);
ResultTy = ResultTy.getNonLValueExprType(Context);

CallExpr *TheCall;
if (Method->isInstance())
TheCall = CXXOperatorCallExpr::Create(Context, OO_Call, NewFn.get(),
MethodArgs, ResultTy, VK, RParenLoc,
CurFPFeatureOverrides());
else
TheCall = CallExpr::Create(Context, NewFn.get(), MethodArgs, ResultTy, VK,
RParenLoc, CurFPFeatureOverrides());
CallExpr *TheCall = CXXOperatorCallExpr::Create(
Context, OO_Call, NewFn.get(), MethodArgs, ResultTy, VK, RParenLoc,
CurFPFeatureOverrides());

if (CheckCallReturnType(Method->getReturnType(), LParenLoc, TheCall, Method))
return true;
Expand Down
44 changes: 24 additions & 20 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7417,9 +7417,9 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
if (ArgResult.isInvalid())
return ExprError();

// Prior to C++20, enforce restrictions on possible template argument
// values.
if (!getLangOpts().CPlusPlus20 && Value.isLValue()) {
if (Value.isLValue()) {
APValue::LValueBase Base = Value.getLValueBase();
auto *VD = const_cast<ValueDecl *>(Base.dyn_cast<const ValueDecl *>());
// For a non-type template-parameter of pointer or reference type,
// the value of the constant expression shall not refer to
assert(ParamType->isPointerType() || ParamType->isReferenceType() ||
Expand All @@ -7428,33 +7428,37 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
// -- a string literal
// -- the result of a typeid expression, or
// -- a predefined __func__ variable
APValue::LValueBase Base = Value.getLValueBase();
auto *VD = const_cast<ValueDecl *>(Base.dyn_cast<const ValueDecl *>());
if (Base &&
(!VD ||
isa<LifetimeExtendedTemporaryDecl, UnnamedGlobalConstantDecl>(VD))) {
Diag(Arg->getBeginLoc(), diag::err_template_arg_not_decl_ref)
<< Arg->getSourceRange();
return ExprError();
}
// -- a subobject [until C++20]
if (Value.hasLValuePath() && Value.getLValuePath().size() == 1 &&
VD && VD->getType()->isArrayType() &&

if (Value.hasLValuePath() && Value.getLValuePath().size() == 1 && VD &&
VD->getType()->isArrayType() &&
Value.getLValuePath()[0].getAsArrayIndex() == 0 &&
!Value.isLValueOnePastTheEnd() && ParamType->isPointerType()) {
// Per defect report (no number yet):
// ... other than a pointer to the first element of a complete array
// object.
} else if (!Value.hasLValuePath() || Value.getLValuePath().size() ||
Value.isLValueOnePastTheEnd()) {
Diag(StartLoc, diag::err_non_type_template_arg_subobject)
<< Value.getAsString(Context, ParamType);
return ExprError();
SugaredConverted = TemplateArgument(VD, ParamType);
CanonicalConverted = TemplateArgument(
cast<ValueDecl>(VD->getCanonicalDecl()), CanonParamType);
return ArgResult.get();
}

// -- a subobject [until C++20]
if (!getLangOpts().CPlusPlus20) {
if (!Value.hasLValuePath() || Value.getLValuePath().size() ||
Value.isLValueOnePastTheEnd()) {
Diag(StartLoc, diag::err_non_type_template_arg_subobject)
<< Value.getAsString(Context, ParamType);
return ExprError();
}
assert((VD || !ParamType->isReferenceType()) &&
"null reference should not be a constant expression");
assert((!VD || !ParamType->isNullPtrType()) &&
"non-null value of type nullptr_t?");
}
assert((VD || !ParamType->isReferenceType()) &&
"null reference should not be a constant expression");
assert((!VD || !ParamType->isNullPtrType()) &&
"non-null value of type nullptr_t?");
}

if (Value.isAddrLabelDiff())
Expand Down
13 changes: 2 additions & 11 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3049,7 +3049,6 @@ bool Sema::SubstDefaultArgument(
// default argument expression appears.
ContextRAII SavedContext(*this, FD);
std::unique_ptr<LocalInstantiationScope> LIS;
MultiLevelTemplateArgumentList NewTemplateArgs = TemplateArgs;

if (ForCallExpr) {
// When instantiating a default argument due to use in a call expression,
Expand All @@ -3062,19 +3061,11 @@ bool Sema::SubstDefaultArgument(
/*ForDefinition*/ false);
if (addInstantiatedParametersToScope(FD, PatternFD, *LIS, TemplateArgs))
return true;
if (FD->isOutOfLine()) {
TemplateArgumentList *CurrentTemplateArgumentList =
TemplateArgumentList::CreateCopy(getASTContext(),
TemplateArgs.getInnermost());
NewTemplateArgs = getTemplateInstantiationArgs(
FD, FD->getDeclContext(), /*Final=*/false,
CurrentTemplateArgumentList, /*RelativeToPrimary=*/true);
}
}

runWithSufficientStackSpace(Loc, [&] {
Result = SubstInitializer(PatternExpr, NewTemplateArgs,
/*DirectInit*/ false);
Result = SubstInitializer(PatternExpr, TemplateArgs,
/*DirectInit*/false);
});
}
if (Result.isInvalid())
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -10075,6 +10075,12 @@ TreeTransform<Derived>::TransformOMPRelaxedClause(OMPRelaxedClause *C) {
return C;
}

template <typename Derived>
OMPClause *TreeTransform<Derived>::TransformOMPWeakClause(OMPWeakClause *C) {
// No need to rebuild this clause, no template-dependent parameters.
return C;
}

template <typename Derived>
OMPClause *
TreeTransform<Derived>::TransformOMPThreadsClause(OMPThreadsClause *C) {
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10349,6 +10349,9 @@ OMPClause *OMPClauseReader::readClause() {
case llvm::omp::OMPC_relaxed:
C = new (Context) OMPRelaxedClause();
break;
case llvm::omp::OMPC_weak:
C = new (Context) OMPWeakClause();
break;
case llvm::omp::OMPC_threads:
C = new (Context) OMPThreadsClause();
break;
Expand Down Expand Up @@ -10747,6 +10750,8 @@ void OMPClauseReader::VisitOMPReleaseClause(OMPReleaseClause *) {}

void OMPClauseReader::VisitOMPRelaxedClause(OMPRelaxedClause *) {}

void OMPClauseReader::VisitOMPWeakClause(OMPWeakClause *) {}

void OMPClauseReader::VisitOMPThreadsClause(OMPThreadsClause *) {}

void OMPClauseReader::VisitOMPSIMDClause(OMPSIMDClause *) {}
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Serialization/ASTWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6698,6 +6698,8 @@ void OMPClauseWriter::VisitOMPReleaseClause(OMPReleaseClause *) {}

void OMPClauseWriter::VisitOMPRelaxedClause(OMPRelaxedClause *) {}

void OMPClauseWriter::VisitOMPWeakClause(OMPWeakClause *) {}

void OMPClauseWriter::VisitOMPThreadsClause(OMPThreadsClause *) {}

void OMPClauseWriter::VisitOMPSIMDClause(OMPSIMDClause *) {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2992,12 +2992,16 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(

// char *realpath(const char *restrict file_name,
// char *restrict resolved_name);
// FIXME: Improve for errno modeling.
// FIXME: If the argument 'resolved_name' is not NULL, macro 'PATH_MAX'
// should be defined in "limits.h" to guarrantee a success.
addToFunctionSummaryMap(
"realpath",
Signature(ArgTypes{ConstCharPtrRestrictTy, CharPtrRestrictTy},
RetType{CharPtrTy}),
Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
Summary(NoEvalCall)
.Case({NotNull(Ret)}, ErrnoMustNotBeChecked, GenericSuccessMsg)
.Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0))));

QualType CharPtrConstPtr = getPointerTy(getConstTy(CharPtrTy));

Expand Down
4 changes: 3 additions & 1 deletion clang/lib/StaticAnalyzer/Core/BugReporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2467,7 +2467,9 @@ ProgramStateManager &PathSensitiveBugReporter::getStateManager() const {
return Eng.getStateManager();
}

BugReporter::BugReporter(BugReporterData &d) : D(d) {}
BugReporter::BugReporter(BugReporterData &D)
: D(D), UserSuppressions(D.getASTContext()) {}

BugReporter::~BugReporter() {
// Make sure reports are flushed.
assert(StrBugTypes.empty() &&
Expand Down
10 changes: 9 additions & 1 deletion clang/lib/StaticAnalyzer/Core/BugSuppression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,17 @@ bool BugSuppression::isSuppressed(const BugReport &R) {
bool BugSuppression::isSuppressed(const PathDiagnosticLocation &Location,
const Decl *DeclWithIssue,
DiagnosticIdentifierList Hashtags) {
if (!Location.isValid() || DeclWithIssue == nullptr)
if (!Location.isValid())
return false;

if (!DeclWithIssue) {
// FIXME: This defeats the purpose of passing DeclWithIssue to begin with.
// If this branch is ever hit, we're re-doing all the work we've already
// done as well as perform a lot of work we'll never need.
// Gladly, none of our on-by-default checkers currently need it.
DeclWithIssue = ACtx.getTranslationUnitDecl();
}

// While some warnings are attached to AST nodes (mostly path-sensitive
// checks), others are simply associated with a plain source location
// or range. Figuring out the node based on locations can be tricky,
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,10 @@ ModuleDepCollector::getInvocationAdjustedForModuleBuildWithoutOutputs(
ScanInstance.getFileManager().getFile(Deps.ClangModuleMapFile);
assert(CurrentModuleMapEntry && "module map file entry not found");

// Remove directly passed modulemap files. They will get added back if they
// were actually used.
CI.getMutFrontendOpts().ModuleMapFiles.clear();

auto DepModuleMapFiles = collectModuleMapFiles(Deps.ClangModuleDeps);
for (StringRef ModuleMapFile : Deps.ModuleMapFileDeps) {
// TODO: Track these as `FileEntryRef` to simplify the equality check below.
Expand Down
19 changes: 17 additions & 2 deletions clang/test/AST/Interp/complex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,31 @@ static_assert(__real(12u) == 12u, "");
static_assert(__imag(4.0) == 0.0, "");
static_assert(__imag(13) == 0, "");

constexpr int ignoredCast() {

constexpr _Complex long L1 = D;
static_assert(__real(L1) == 1.0, "");
static_assert(__imag(L1) == 3.0, "");

constexpr _Complex short I4 = L1;
static_assert(__real(I4) == 1, "");
static_assert(__imag(I4) == 3, "");

constexpr _Complex float D3 = D;
static_assert(__real(D3) == 1.0, "");
static_assert(__imag(D3) == 3.0, "");


constexpr int ignored() {
I2;
(int)I2;
(float)I2;
D1;
(int)D1;
(double)D1;
(_Complex float)I2;
return 0;
}
static_assert(ignoredCast() == 0, "");
static_assert(ignored() == 0, "");
static_assert((int)I1 == 1, "");
static_assert((float)D == 1.0f, "");

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

// expected-no-diagnostics

namespace IntOrEnum {
const int k = 0;
const int &p = k;
Expand All @@ -17,8 +15,10 @@ template struct C<cval>;

/// FIXME: This example does not get properly diagnosed in the new interpreter.
extern const int recurse1;
const int recurse2 = recurse1; // ref-note {{here}}
const int recurse2 = recurse1; // both-note {{here}}
const int recurse1 = 1;
int array1[recurse1];
int array2[recurse2]; // ref-warning 2{{variable length array}} \
// ref-note {{initializer of 'recurse2' is not a constant expression}}
int array2[recurse2]; // both-warning {{variable length arrays in C++}} \
// both-note {{initializer of 'recurse2' is not a constant expression}} \
// expected-error {{variable length array declaration not allowed at file scope}} \
// ref-warning {{variable length array folded to constant array as an extension}}
23 changes: 20 additions & 3 deletions clang/test/AST/Interp/cxx17.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++17 -verify %s
// RUN: %clang_cc1 -std=c++17 -verify=ref %s

// ref-no-diagnostics
// expected-no-diagnostics

struct F { int a; int b;};
constexpr F getF() {
return {12, 3};
Expand Down Expand Up @@ -83,3 +80,23 @@ constexpr int b() {
return a[0] + a[1];
}
static_assert(b() == 11);

/// The diagnostics between the two interpreters are different here.
struct S { int a; };
constexpr S getS() { // expected-error {{constexpr function never produces a constant expression}} \\
// ref-error {{constexpr function never produces a constant expression}}
(void)(1/0); // expected-note 2{{division by zero}} \
// expected-warning {{division by zero}} \
// ref-note 2{{division by zero}} \
// ref-warning {{division by zero}}
return S{12};
}
constexpr S s = getS(); // expected-error {{must be initialized by a constant expression}} \
// expected-note {{in call to 'getS()'}} \
// ref-error {{must be initialized by a constant expression}} \\
// ref-note {{in call to 'getS()'}} \
// ref-note {{declared here}}
static_assert(s.a == 12, ""); // expected-error {{not an integral constant expression}} \
// expected-note {{read of uninitialized object}} \
// ref-error {{not an integral constant expression}} \
// ref-note {{initializer of 's' is not a constant expression}}
24 changes: 17 additions & 7 deletions clang/test/AST/Interp/cxx23.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,35 @@
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=expected20 %s -fexperimental-new-constant-interpreter
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -verify=expected23 %s -fexperimental-new-constant-interpreter


/// FIXME: The new interpreter is missing all the 'control flows through...' diagnostics.

constexpr int f(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
// ref23-error {{constexpr function never produces a constant expression}}
// ref23-error {{constexpr function never produces a constant expression}} \
// expected20-error {{constexpr function never produces a constant expression}} \
// expected23-error {{constexpr function never produces a constant expression}}
static const int m = n; // ref20-note {{control flows through the definition of a static variable}} \
// ref20-warning {{is a C++23 extension}} \
// ref23-note {{control flows through the definition of a static variable}} \
// expected20-warning {{is a C++23 extension}}
// expected20-warning {{is a C++23 extension}} \
// expected20-note {{declared here}} \
// expected23-note {{declared here}}

return m;
return m; // expected20-note {{initializer of 'm' is not a constant expression}} \
// expected23-note {{initializer of 'm' is not a constant expression}}
}
constexpr int g(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
// ref23-error {{constexpr function never produces a constant expression}}
// ref23-error {{constexpr function never produces a constant expression}} \
// expected20-error {{constexpr function never produces a constant expression}} \
// expected23-error {{constexpr function never produces a constant expression}}
thread_local const int m = n; // ref20-note {{control flows through the definition of a thread_local variable}} \
// ref20-warning {{is a C++23 extension}} \
// ref23-note {{control flows through the definition of a thread_local variable}} \
// expected20-warning {{is a C++23 extension}}
return m;
// expected20-warning {{is a C++23 extension}} \
// expected20-note {{declared here}} \
// expected23-note {{declared here}}
return m; // expected20-note {{initializer of 'm' is not a constant expression}} \
// expected23-note {{initializer of 'm' is not a constant expression}}

}

constexpr int c_thread_local(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
Expand Down
Loading