19 changes: 19 additions & 0 deletions clang/include/clang/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -2333,6 +2333,11 @@ class UnaryOperator final
return getTrailingFPFeatures();
}

/// Get the store FPOptionsOverride or default if not stored.
FPOptionsOverride getStoredFPFeaturesOrDefault() const {
return hasStoredFPFeatures() ? getStoredFPFeatures() : FPOptionsOverride();
}

protected:
/// Set FPFeatures in trailing storage, used by Serialization & ASTImporter.
void setStoredFPFeatures(FPOptionsOverride F) { getTrailingFPFeatures() = F; }
Expand Down Expand Up @@ -3096,6 +3101,11 @@ class CallExpr : public Expr {
*getTrailingFPFeatures() = F;
}

/// Get the store FPOptionsOverride or default if not stored.
FPOptionsOverride getStoredFPFeaturesOrDefault() const {
return hasStoredFPFeatures() ? getStoredFPFeatures() : FPOptionsOverride();
}

/// Get the FP features status of this operator. Only meaningful for
/// operations on floating point types.
FPOptions getFPFeaturesInEffect(const LangOptions &LO) const {
Expand Down Expand Up @@ -3592,6 +3602,11 @@ class CastExpr : public Expr {
return *getTrailingFPFeatures();
}

/// Get the store FPOptionsOverride or default if not stored.
FPOptionsOverride getStoredFPFeaturesOrDefault() const {
return hasStoredFPFeatures() ? getStoredFPFeatures() : FPOptionsOverride();
}

/// Get the FP features status of this operation. Only meaningful for
/// operations on floating point types.
FPOptions getFPFeaturesInEffect(const LangOptions &LO) const {
Expand Down Expand Up @@ -4038,6 +4053,10 @@ class BinaryOperator : public Expr {
assert(BinaryOperatorBits.HasFPFeatures);
*getTrailingFPFeatures() = F;
}
/// Get the store FPOptionsOverride or default if not stored.
FPOptionsOverride getStoredFPFeaturesOrDefault() const {
return hasStoredFPFeatures() ? getStoredFPFeatures() : FPOptionsOverride();
}

/// Get the FP features status of this operator. Only meaningful for
/// operations on floating point types.
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -3032,6 +3032,9 @@ DEF_TRAVERSE_STMT(OMPTileDirective,
DEF_TRAVERSE_STMT(OMPUnrollDirective,
{ TRY_TO(TraverseOMPExecutableDirective(S)); })

DEF_TRAVERSE_STMT(OMPReverseDirective,
{ TRY_TO(TraverseOMPExecutableDirective(S)); })

DEF_TRAVERSE_STMT(OMPForDirective,
{ TRY_TO(TraverseOMPExecutableDirective(S)); })

Expand Down
11 changes: 8 additions & 3 deletions clang/include/clang/AST/Stmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -460,10 +460,10 @@ class alignas(void *) Stmt {
unsigned : NumExprBits;

static_assert(
llvm::APFloat::S_MaxSemantics < 16,
"Too many Semantics enum values to fit in bitfield of size 4");
llvm::APFloat::S_MaxSemantics < 32,
"Too many Semantics enum values to fit in bitfield of size 5");
LLVM_PREFERRED_TYPE(llvm::APFloat::Semantics)
unsigned Semantics : 4; // Provides semantics for APFloat construction
unsigned Semantics : 5; // Provides semantics for APFloat construction
LLVM_PREFERRED_TYPE(bool)
unsigned IsExact : 1;
};
Expand Down Expand Up @@ -1658,6 +1658,11 @@ class CompoundStmt final
return *getTrailingObjects<FPOptionsOverride>();
}

/// Get the store FPOptionsOverride or default if not stored.
FPOptionsOverride getStoredFPFeaturesOrDefault() const {
return hasStoredFPFeatures() ? getStoredFPFeatures() : FPOptionsOverride();
}

using body_iterator = Stmt **;
using body_range = llvm::iterator_range<body_iterator>;

Expand Down
69 changes: 67 additions & 2 deletions clang/include/clang/AST/StmtOpenMP.h
Original file line number Diff line number Diff line change
Expand Up @@ -1007,8 +1007,9 @@ class OMPLoopTransformationDirective : public OMPLoopBasedDirective {
Stmt *getPreInits() const;

static bool classof(const Stmt *T) {
return T->getStmtClass() == OMPTileDirectiveClass ||
T->getStmtClass() == OMPUnrollDirectiveClass;
Stmt::StmtClass C = T->getStmtClass();
return C == OMPTileDirectiveClass || C == OMPUnrollDirectiveClass ||
C == OMPReverseDirectiveClass;
}
};

Expand Down Expand Up @@ -5711,6 +5712,70 @@ class OMPUnrollDirective final : public OMPLoopTransformationDirective {
}
};

/// Represents the '#pragma omp reverse' loop transformation directive.
///
/// \code
/// #pragma omp reverse
/// for (int i = 0; i < n; ++i)
/// ...
/// \endcode
class OMPReverseDirective final : public OMPLoopTransformationDirective {
friend class ASTStmtReader;
friend class OMPExecutableDirective;

/// Offsets of child members.
enum {
PreInitsOffset = 0,
TransformedStmtOffset,
};

explicit OMPReverseDirective(SourceLocation StartLoc, SourceLocation EndLoc)
: OMPLoopTransformationDirective(OMPReverseDirectiveClass,
llvm::omp::OMPD_reverse, StartLoc,
EndLoc, 1) {}

void setPreInits(Stmt *PreInits) {
Data->getChildren()[PreInitsOffset] = PreInits;
}

void setTransformedStmt(Stmt *S) {
Data->getChildren()[TransformedStmtOffset] = S;
}

public:
/// Create a new AST node representation for '#pragma omp reverse'.
///
/// \param C Context of the AST.
/// \param StartLoc Location of the introducer (e.g. the 'omp' token).
/// \param EndLoc Location of the directive's end (e.g. the tok::eod).
/// \param AssociatedStmt The outermost associated loop.
/// \param TransformedStmt The loop nest after tiling, or nullptr in
/// dependent contexts.
/// \param PreInits Helper preinits statements for the loop nest.
static OMPReverseDirective *
Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
Stmt *AssociatedStmt, Stmt *TransformedStmt, Stmt *PreInits);

/// Build an empty '#pragma omp reverse' AST node for deserialization.
///
/// \param C Context of the AST.
/// \param NumClauses Number of clauses to allocate.
static OMPReverseDirective *CreateEmpty(const ASTContext &C);

/// Gets/sets the associated loops after the transformation, i.e. after
/// de-sugaring.
Stmt *getTransformedStmt() const {
return Data->getChildren()[TransformedStmtOffset];
}

/// Return preinits statement.
Stmt *getPreInits() const { return Data->getChildren()[PreInitsOffset]; }

static bool classof(const Stmt *T) {
return T->getStmtClass() == OMPReverseDirectiveClass;
}
};

/// This represents '#pragma omp scan' directive.
///
/// \code
Expand Down
69 changes: 40 additions & 29 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ class TemplateArgument;
class TemplateArgumentListInfo;
class TemplateArgumentLoc;
class TemplateTypeParmDecl;
template <typename> class TreeTransform;
class TypedefNameDecl;
class UnresolvedUsingTypenameDecl;
class UsingShadowDecl;
Expand Down Expand Up @@ -1618,6 +1617,10 @@ class QualType {
QualType stripObjCKindOfType(const ASTContext &ctx) const;

/// Remove all qualifiers including _Atomic.
///
/// Like getUnqualifiedType(), the type may still be qualified if it is a
/// sugared array type. To strip qualifiers even from within a sugared array
/// type, use in conjunction with ASTContext::getUnqualifiedArrayType.
QualType getAtomicUnqualifiedType() const;

private:
Expand Down Expand Up @@ -2105,8 +2108,8 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {

LLVM_PREFERRED_TYPE(TypeBitfields)
unsigned : NumTypeBits;
LLVM_PREFERRED_TYPE(bool)
unsigned IsUnqual : 1; // If true: typeof_unqual, else: typeof
LLVM_PREFERRED_TYPE(TypeOfKind)
unsigned Kind : 1;
};

class UsingBitfields {
Expand Down Expand Up @@ -4897,7 +4900,6 @@ class FunctionEffectsRef {
return !(LHS == RHS);
}

void Profile(llvm::FoldingSetNodeID &ID) const;
void dump(llvm::raw_ostream &OS) const;
};

Expand Down Expand Up @@ -4967,8 +4969,8 @@ class FunctionProtoType final
FunctionProtoType, QualType, SourceLocation,
FunctionType::FunctionTypeExtraBitfields,
FunctionType::FunctionTypeArmAttributes, FunctionType::ExceptionType,
Expr *, FunctionDecl *, FunctionType::ExtParameterInfo,
FunctionEffect, EffectConditionExpr, Qualifiers> {
Expr *, FunctionDecl *, FunctionType::ExtParameterInfo, Qualifiers,
FunctionEffect, EffectConditionExpr> {
friend class ASTContext; // ASTContext creates these.
friend TrailingObjects;

Expand Down Expand Up @@ -4999,21 +5001,21 @@ class FunctionProtoType final
// an ExtParameterInfo for each of the parameters. Present if and
// only if hasExtParameterInfos() is true.
//
// * Optionally a Qualifiers object to represent extra qualifiers that can't
// be represented by FunctionTypeBitfields.FastTypeQuals. Present if and
// only if hasExtQualifiers() is true.
//
// * Optionally, an array of getNumFunctionEffects() FunctionEffect.
// Present only when getNumFunctionEffects() > 0
//
// * Optionally, an array of getNumFunctionEffects() EffectConditionExpr.
// Present only when getNumFunctionEffectConditions() > 0.
//
// * Optionally a Qualifiers object to represent extra qualifiers that can't
// be represented by FunctionTypeBitfields.FastTypeQuals. Present if and
// only if hasExtQualifiers() is true.
//
// The optional FunctionTypeExtraBitfields has to be before the data
// related to the exception specification since it contains the number
// of exception types.
//
// We put the ExtParameterInfos last. If all were equal, it would make
// We put the ExtParameterInfos later. If all were equal, it would make
// more sense to put these before the exception specification, because
// it's much easier to skip past them compared to the elaborate switch
// required to skip the exception specification. However, all is not
Expand Down Expand Up @@ -5130,6 +5132,10 @@ class FunctionProtoType final
return hasExtParameterInfos() ? getNumParams() : 0;
}

unsigned numTrailingObjects(OverloadToken<Qualifiers>) const {
return hasExtQualifiers() ? 1 : 0;
}

unsigned numTrailingObjects(OverloadToken<FunctionEffect>) const {
return getNumFunctionEffects();
}
Expand Down Expand Up @@ -5661,19 +5667,20 @@ class MacroQualifiedType : public Type {
/// extension) or a `typeof_unqual` expression (a C23 feature).
class TypeOfExprType : public Type {
Expr *TOExpr;
const ASTContext &Context;

protected:
friend class ASTContext; // ASTContext creates these.

TypeOfExprType(Expr *E, TypeOfKind Kind, QualType Can = QualType());
TypeOfExprType(const ASTContext &Context, Expr *E, TypeOfKind Kind,
QualType Can = QualType());

public:
Expr *getUnderlyingExpr() const { return TOExpr; }

/// Returns the kind of 'typeof' type this is.
TypeOfKind getKind() const {
return TypeOfBits.IsUnqual ? TypeOfKind::Unqualified
: TypeOfKind::Qualified;
return static_cast<TypeOfKind>(TypeOfBits.Kind);
}

/// Remove a single level of sugar.
Expand All @@ -5694,7 +5701,8 @@ class TypeOfExprType : public Type {
class DependentTypeOfExprType : public TypeOfExprType,
public llvm::FoldingSetNode {
public:
DependentTypeOfExprType(Expr *E, TypeOfKind Kind) : TypeOfExprType(E, Kind) {}
DependentTypeOfExprType(const ASTContext &Context, Expr *E, TypeOfKind Kind)
: TypeOfExprType(Context, E, Kind) {}

void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context) {
Profile(ID, Context, getUnderlyingExpr(),
Expand All @@ -5711,32 +5719,23 @@ class TypeOfType : public Type {
friend class ASTContext; // ASTContext creates these.

QualType TOType;
const ASTContext &Context;

TypeOfType(QualType T, QualType Can, TypeOfKind Kind)
: Type(TypeOf,
Kind == TypeOfKind::Unqualified ? Can.getAtomicUnqualifiedType()
: Can,
T->getDependence()),
TOType(T) {
TypeOfBits.IsUnqual = Kind == TypeOfKind::Unqualified;
}
TypeOfType(const ASTContext &Context, QualType T, QualType Can,
TypeOfKind Kind);

public:
QualType getUnmodifiedType() const { return TOType; }

/// Remove a single level of sugar.
QualType desugar() const {
QualType QT = getUnmodifiedType();
return TypeOfBits.IsUnqual ? QT.getAtomicUnqualifiedType() : QT;
}
QualType desugar() const;

/// Returns whether this type directly provides sugar.
bool isSugared() const { return true; }

/// Returns the kind of 'typeof' type this is.
TypeOfKind getKind() const {
return TypeOfBits.IsUnqual ? TypeOfKind::Unqualified
: TypeOfKind::Qualified;
return static_cast<TypeOfKind>(TypeOfBits.Kind);
}

static bool classof(const Type *T) { return T->getTypeClass() == TypeOf; }
Expand Down Expand Up @@ -8619,6 +8618,18 @@ QualType DecayedType::getPointeeType() const {
void FixedPointValueToString(SmallVectorImpl<char> &Str, llvm::APSInt Val,
unsigned Scale);

inline FunctionEffectsRef FunctionEffectsRef::get(QualType QT) {
while (true) {
QualType Pointee = QT->getPointeeType();
if (Pointee.isNull())
break;
QT = Pointee;
}
if (const auto *FPT = QT->getAs<FunctionProtoType>())
return FPT->getFunctionEffects();
return {};
}

} // namespace clang

#endif // LLVM_CLANG_AST_TYPE_H
1 change: 1 addition & 0 deletions clang/include/clang/Basic/Builtins.def
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,4 @@
// M_0, ..., M_k as payload
// z -> this is a function in (possibly-versioned) namespace std
// E -> this function can be constant evaluated by Clang frontend
// G -> this is a C++20 consteval function
5 changes: 5 additions & 0 deletions clang/include/clang/Basic/Builtins.h
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,11 @@ class Context {
return strchr(getRecord(ID).Attributes, 'E') != nullptr;
}

/// Returns true if this is an immediate (consteval) function
bool isImmediate(unsigned ID) const {
return strchr(getRecord(ID).Attributes, 'G') != nullptr;
}

private:
const Info &getRecord(unsigned ID) const;

Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/BuiltinsBase.td
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ class VScanfFormat<int I> : IndexedAttribute<"S", I>;

// Builtin can be constant evaluated
def Constexpr : Attribute<"E">;
// Builtin is immediate and must be constant evaluated. Implies Constexpr, and will only be supported in C++20 mode.
def Consteval : Attribute<"EG">;

// Builtin kinds
// =============
Expand Down
30 changes: 0 additions & 30 deletions clang/include/clang/Basic/BuiltinsX86.def
Original file line number Diff line number Diff line change
Expand Up @@ -37,36 +37,6 @@ TARGET_BUILTIN(__builtin_ia32_undef512, "V8d", "ncV:512:", "")
TARGET_BUILTIN(__builtin_ia32_readeflags_u32, "Ui", "n", "")
TARGET_BUILTIN(__builtin_ia32_writeeflags_u32, "vUi", "n", "")

// 3DNow!
//
TARGET_BUILTIN(__builtin_ia32_femms, "v", "n", "3dnow")
TARGET_BUILTIN(__builtin_ia32_pavgusb, "V8cV8cV8c", "ncV:64:", "3dnow")
TARGET_BUILTIN(__builtin_ia32_pf2id, "V2iV2f", "ncV:64:", "3dnow")
TARGET_BUILTIN(__builtin_ia32_pfacc, "V2fV2fV2f", "ncV:64:", "3dnow")
TARGET_BUILTIN(__builtin_ia32_pfadd, "V2fV2fV2f", "ncV:64:", "3dnow")
TARGET_BUILTIN(__builtin_ia32_pfcmpeq, "V2iV2fV2f", "ncV:64:", "3dnow")
TARGET_BUILTIN(__builtin_ia32_pfcmpge, "V2iV2fV2f", "ncV:64:", "3dnow")
TARGET_BUILTIN(__builtin_ia32_pfcmpgt, "V2iV2fV2f", "ncV:64:", "3dnow")
TARGET_BUILTIN(__builtin_ia32_pfmax, "V2fV2fV2f", "ncV:64:", "3dnow")
TARGET_BUILTIN(__builtin_ia32_pfmin, "V2fV2fV2f", "ncV:64:", "3dnow")
TARGET_BUILTIN(__builtin_ia32_pfmul, "V2fV2fV2f", "ncV:64:", "3dnow")
TARGET_BUILTIN(__builtin_ia32_pfrcp, "V2fV2f", "ncV:64:", "3dnow")
TARGET_BUILTIN(__builtin_ia32_pfrcpit1, "V2fV2fV2f", "ncV:64:", "3dnow")
TARGET_BUILTIN(__builtin_ia32_pfrcpit2, "V2fV2fV2f", "ncV:64:", "3dnow")
TARGET_BUILTIN(__builtin_ia32_pfrsqrt, "V2fV2f", "ncV:64:", "3dnow")
TARGET_BUILTIN(__builtin_ia32_pfrsqit1, "V2fV2fV2f", "ncV:64:", "3dnow")
TARGET_BUILTIN(__builtin_ia32_pfsub, "V2fV2fV2f", "ncV:64:", "3dnow")
TARGET_BUILTIN(__builtin_ia32_pfsubr, "V2fV2fV2f", "ncV:64:", "3dnow")
TARGET_BUILTIN(__builtin_ia32_pi2fd, "V2fV2i", "ncV:64:", "3dnow")
TARGET_BUILTIN(__builtin_ia32_pmulhrw, "V4sV4sV4s", "ncV:64:", "3dnow")
// 3DNow! Extensions (3dnowa).
TARGET_BUILTIN(__builtin_ia32_pf2iw, "V2iV2f", "ncV:64:", "3dnowa")
TARGET_BUILTIN(__builtin_ia32_pfnacc, "V2fV2fV2f", "ncV:64:", "3dnowa")
TARGET_BUILTIN(__builtin_ia32_pfpnacc, "V2fV2fV2f", "ncV:64:", "3dnowa")
TARGET_BUILTIN(__builtin_ia32_pi2fw, "V2fV2i", "ncV:64:", "3dnowa")
TARGET_BUILTIN(__builtin_ia32_pswapdsf, "V2fV2f", "ncV:64:", "3dnowa")
TARGET_BUILTIN(__builtin_ia32_pswapdsi, "V2iV2i", "ncV:64:", "3dnowa")

// MMX
//
// All MMX instructions will be generated via builtins. Any MMX vector
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/DiagnosticDriverKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,10 @@ def warn_drv_deprecated_arg : Warning<
def warn_drv_deprecated_arg_no_relaxed_template_template_args : Warning<
"argument '-fno-relaxed-template-template-args' is deprecated">,
InGroup<DeprecatedNoRelaxedTemplateTemplateArgs>;
def warn_drv_deprecated_arg_ofast : Warning<
"argument '-Ofast' is deprecated; use '-O3 -ffast math' for the same behavior,"
" or '-O3' to enable only conforming optimizations">,
InGroup<DeprecatedOFast>;
def warn_drv_deprecated_custom : Warning<
"argument '%0' is deprecated, %1">, InGroup<Deprecated>;
def warn_drv_assuming_mfloat_abi_is : Warning<
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ def EnumConversion : DiagGroup<"enum-conversion",
EnumFloatConversion,
EnumCompareConditional]>;
def DeprecatedNoRelaxedTemplateTemplateArgs : DiagGroup<"deprecated-no-relaxed-template-template-args">;
def DeprecatedOFast : DiagGroup<"deprecated-ofast">;
def ObjCSignedCharBoolImplicitIntConversion :
DiagGroup<"objc-signed-char-bool-implicit-int-conversion">;
def Shorten64To32 : DiagGroup<"shorten-64-to-32">;
Expand Down Expand Up @@ -228,6 +229,7 @@ def Deprecated : DiagGroup<"deprecated", [DeprecatedAnonEnumEnumConversion,
DeprecatedPragma,
DeprecatedRegister,
DeprecatedNoRelaxedTemplateTemplateArgs,
DeprecatedOFast,
DeprecatedThisCapture,
DeprecatedType,
DeprecatedVolatile,
Expand Down Expand Up @@ -449,6 +451,7 @@ def LogicalNotParentheses: DiagGroup<"logical-not-parentheses">;
def ShiftOpParentheses: DiagGroup<"shift-op-parentheses">;
def OverloadedShiftOpParentheses: DiagGroup<"overloaded-shift-op-parentheses">;
def DanglingAssignment: DiagGroup<"dangling-assignment">;
def DanglingAssignmentGsl : DiagGroup<"dangling-assignment-gsl">;
def DanglingElse: DiagGroup<"dangling-else">;
def DanglingField : DiagGroup<"dangling-field">;
def DanglingInitializerList : DiagGroup<"dangling-initializer-list">;
Expand All @@ -457,6 +460,7 @@ def ReturnStackAddress : DiagGroup<"return-stack-address">;
// Name of this warning in GCC
def : DiagGroup<"return-local-addr", [ReturnStackAddress]>;
def Dangling : DiagGroup<"dangling", [DanglingAssignment,
DanglingAssignmentGsl,
DanglingField,
DanglingInitializerList,
DanglingGsl,
Expand Down
10 changes: 8 additions & 2 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -6075,7 +6075,7 @@ def note_extern_c_global_conflict : Note<
def note_extern_c_begins_here : Note<
"extern \"C\" language linkage specification begins here">;
def warn_weak_import : Warning <
"an already-declared variable is made a weak_import declaration %0">;
"%0 cannot be declared 'weak_import' because its definition has been provided">;
def ext_static_non_static : Extension<
"redeclaring non-static %0 as static is a Microsoft extension">,
InGroup<MicrosoftRedeclareStatic>;
Expand Down Expand Up @@ -7249,7 +7249,10 @@ def err_array_init_utf8_string_into_char : Error<
def warn_cxx20_compat_utf8_string : Warning<
"type of UTF-8 string literal will change from array of const char to "
"array of const char8_t in C++20">, InGroup<CXX20Compat>, DefaultIgnore;
def note_cxx20_compat_utf8_string_remove_u8 : Note<
def warn_c23_compat_utf8_string : Warning<
"type of UTF-8 string literal will change from array of char to "
"array of char8_t in C23">, InGroup<C23Compat>, DefaultIgnore;
def note_cxx20_c23_compat_utf8_string_remove_u8 : Note<
"remove 'u8' prefix to avoid a change of behavior; "
"Clang encodes unprefixed narrow string literals as UTF-8">;
def err_array_init_different_type : Error<
Expand Down Expand Up @@ -10121,6 +10124,9 @@ def warn_dangling_lifetime_pointer : Warning<
"object backing the pointer "
"will be destroyed at the end of the full-expression">,
InGroup<DanglingGsl>;
def warn_dangling_lifetime_pointer_assignment : Warning<"object backing the "
"pointer %0 will be destroyed at the end of the full-expression">,
InGroup<DanglingAssignmentGsl>, DefaultIgnore;
def warn_new_dangling_initializer_list : Warning<
"array backing "
"%select{initializer list subobject of the allocated object|"
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/StmtNodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ def OMPSimdDirective : StmtNode<OMPLoopDirective>;
def OMPLoopTransformationDirective : StmtNode<OMPLoopBasedDirective, 1>;
def OMPTileDirective : StmtNode<OMPLoopTransformationDirective>;
def OMPUnrollDirective : StmtNode<OMPLoopTransformationDirective>;
def OMPReverseDirective : StmtNode<OMPLoopTransformationDirective>;
def OMPForDirective : StmtNode<OMPLoopDirective>;
def OMPForSimdDirective : StmtNode<OMPLoopDirective>;
def OMPSectionsDirective : StmtNode<OMPExecutableDirective>;
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@ TOK(raw_identifier) // Used only in raw lexing mode.
// C99 6.4.4.2: Floating Constants
TOK(numeric_constant) // 0x123

// Directly holds numerical value. Used to process C23 #embed.
TOK(binary_data)

// C99 6.4.4: Character Constants
TOK(char_constant) // 'a'
TOK(wide_char_constant) // L'b'
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/TokenKinds.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ inline bool isLiteral(TokenKind K) {
return K == tok::numeric_constant || K == tok::char_constant ||
K == tok::wide_char_constant || K == tok::utf8_char_constant ||
K == tok::utf16_char_constant || K == tok::utf32_char_constant ||
isStringLiteral(K) || K == tok::header_name;
isStringLiteral(K) || K == tok::header_name || K == tok::binary_data;
}

/// Return true if this is any of tok::annot_* kinds.
Expand Down
23 changes: 14 additions & 9 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -931,7 +931,9 @@ def O : Joined<["-"], "O">, Group<O_Group>,
def O_flag : Flag<["-"], "O">, Visibility<[ClangOption, CC1Option, FC1Option]>,
Alias<O>, AliasArgs<["1"]>;
def Ofast : Joined<["-"], "Ofast">, Group<O_Group>,
Visibility<[ClangOption, CC1Option, FlangOption]>;
Visibility<[ClangOption, CC1Option, FlangOption]>,
HelpText<"Deprecated; use '-O3 -ffast math' for the same behavior,"
" or '-O3' to enable only conforming optimizations">;
def P : Flag<["-"], "P">,
Visibility<[ClangOption, CC1Option, FlangOption, FC1Option]>,
Group<Preprocessor_Group>,
Expand Down Expand Up @@ -5456,6 +5458,7 @@ def module_file_info : Flag<["-"], "module-file-info">, Flags<[]>,
HelpText<"Provide information about a particular module file">;
def mthumb : Flag<["-"], "mthumb">, Group<m_Group>;
def mtune_EQ : Joined<["-"], "mtune=">, Group<m_Group>,
Visibility<[ClangOption, FlangOption]>,
HelpText<"Only supported on AArch64, PowerPC, RISC-V, SPARC, SystemZ, and X86">;
def multi__module : Flag<["-"], "multi_module">;
def multiply__defined__unused : Separate<["-"], "multiply_defined_unused">;
Expand Down Expand Up @@ -5621,7 +5624,7 @@ def resource_dir_EQ : Joined<["-"], "resource-dir=">, Flags<[NoXarchOption]>,
Alias<resource_dir>;
def rpath : Separate<["-"], "rpath">, Flags<[LinkerInput]>, Group<Link_Group>,
Visibility<[ClangOption, CLOption, DXCOption, FlangOption]>;
def rtlib_EQ : Joined<["-", "--"], "rtlib=">, Visibility<[ClangOption, CLOption]>,
def rtlib_EQ : Joined<["-", "--"], "rtlib=">, Visibility<[ClangOption, CLOption, FlangOption]>,
HelpText<"Compiler runtime library to use">;
def frtlib_add_rpath: Flag<["-"], "frtlib-add-rpath">, Flags<[NoArgumentUnused]>,
Visibility<[ClangOption, FlangOption]>,
Expand Down Expand Up @@ -6134,10 +6137,6 @@ def mno_80387 : Flag<["-"], "mno-80387">, Alias<mno_x87>;
def mno_fp_ret_in_387 : Flag<["-"], "mno-fp-ret-in-387">, Alias<mno_x87>;
def mmmx : Flag<["-"], "mmmx">, Group<m_x86_Features_Group>;
def mno_mmx : Flag<["-"], "mno-mmx">, Group<m_x86_Features_Group>;
def m3dnow : Flag<["-"], "m3dnow">, Group<m_x86_Features_Group>;
def mno_3dnow : Flag<["-"], "mno-3dnow">, Group<m_x86_Features_Group>;
def m3dnowa : Flag<["-"], "m3dnowa">, Group<m_x86_Features_Group>;
def mno_3dnowa : Flag<["-"], "mno-3dnowa">, Group<m_x86_Features_Group>;
def mamx_bf16 : Flag<["-"], "mamx-bf16">, Group<m_x86_Features_Group>;
def mno_amx_bf16 : Flag<["-"], "mno-amx-bf16">, Group<m_x86_Features_Group>;
def mamx_complex : Flag<["-"], "mamx-complex">, Group<m_x86_Features_Group>;
Expand Down Expand Up @@ -6371,6 +6370,12 @@ def mvevpu : Flag<["-"], "mvevpu">, Group<m_ve_Features_Group>,
def mno_vevpu : Flag<["-"], "mno-vevpu">, Group<m_ve_Features_Group>;
} // let Flags = [TargetSpecific]

// Unsupported X86 feature flags (triggers a warning)
def m3dnow : Flag<["-"], "m3dnow">;
def mno_3dnow : Flag<["-"], "mno-3dnow">;
def m3dnowa : Flag<["-"], "m3dnowa">;
def mno_3dnowa : Flag<["-"], "mno-3dnowa">;

// These are legacy user-facing driver-level option spellings. They are always
// aliases for options that are spelled using the more common Unix / GNU flag
// style of double-dash and equals-joined flags.
Expand Down Expand Up @@ -6783,9 +6788,6 @@ def emit_hlfir : Flag<["-"], "emit-hlfir">, Group<Action_Group>,

let Visibility = [CC1Option, CC1AsOption] in {

def tune_cpu : Separate<["-"], "tune-cpu">,
HelpText<"Tune for a specific cpu type">,
MarshallingInfoString<TargetOpts<"TuneCPU">>;
def target_abi : Separate<["-"], "target-abi">,
HelpText<"Target a particular ABI type">,
MarshallingInfoString<TargetOpts<"ABI">>;
Expand All @@ -6812,6 +6814,9 @@ def darwin_target_variant_triple : Separate<["-"], "darwin-target-variant-triple

let Visibility = [CC1Option, CC1AsOption, FC1Option] in {

def tune_cpu : Separate<["-"], "tune-cpu">,
HelpText<"Tune for a specific cpu type">,
MarshallingInfoString<TargetOpts<"TuneCPU">>;
def target_cpu : Separate<["-"], "target-cpu">,
HelpText<"Target a specific cpu type">,
MarshallingInfoString<TargetOpts<"CPU">>;
Expand Down
51 changes: 27 additions & 24 deletions clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,22 +175,25 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> {
// skip classes not inherited as public
if (BaseSpecifier.getAccessSpecifier() != AccessSpecifier::AS_public)
continue;
SymbolReference BaseClass;
if (BaseSpecifier.getType().getTypePtr()->isTemplateTypeParmType()) {
BaseClass.Name = API.copyString(BaseSpecifier.getType().getAsString());
if (auto *TTPTD = BaseSpecifier.getType()
->getAs<TemplateTypeParmType>()
->getDecl()) {
SmallString<128> USR;
index::generateUSRForDecl(TTPTD, USR);
BaseClass.USR = API.copyString(USR);
BaseClass.Source = API.copyString(getOwningModuleName(*TTPTD));
}
if (auto *BaseDecl = BaseSpecifier.getType()->getAsTagDecl()) {
Bases.emplace_back(createSymbolReferenceForDecl(*BaseDecl));
} else {
BaseClass = createSymbolReferenceForDecl(
*BaseSpecifier.getType().getTypePtr()->getAsCXXRecordDecl());
SymbolReference BaseClass;
BaseClass.Name = API.copyString(BaseSpecifier.getType().getAsString(
Decl->getASTContext().getPrintingPolicy()));

if (BaseSpecifier.getType().getTypePtr()->isTemplateTypeParmType()) {
if (auto *TTPTD = BaseSpecifier.getType()
->getAs<TemplateTypeParmType>()
->getDecl()) {
SmallString<128> USR;
index::generateUSRForDecl(TTPTD, USR);
BaseClass.USR = API.copyString(USR);
BaseClass.Source = API.copyString(getOwningModuleName(*TTPTD));
}
}
Bases.emplace_back(BaseClass);
}
Bases.emplace_back(BaseClass);
}
return Bases;
}
Expand Down Expand Up @@ -352,7 +355,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitFunctionDecl(
return true;

// Collect symbol information.
StringRef Name = Decl->getName();
auto Name = Decl->getNameAsString();
SmallString<128> USR;
index::generateUSRForDecl(Decl, USR);
PresumedLoc Loc =
Expand Down Expand Up @@ -666,17 +669,17 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXMethodDecl(
if (FunctionTemplateDecl *TemplateDecl =
Decl->getDescribedFunctionTemplate()) {
API.createRecord<CXXMethodTemplateRecord>(
USR, Decl->getName(), createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Comment,
USR, Decl->getNameAsString(), createHierarchyInformationForDecl(*Decl),
Loc, AvailabilityInfo::createFromDecl(Decl), Comment,
DeclarationFragmentsBuilder::getFragmentsForFunctionTemplate(
TemplateDecl),
SubHeading, DeclarationFragmentsBuilder::getFunctionSignature(Decl),
DeclarationFragmentsBuilder::getAccessControl(TemplateDecl),
Template(TemplateDecl), isInSystemHeader(Decl));
} else if (Decl->getTemplateSpecializationInfo())
API.createRecord<CXXMethodTemplateSpecializationRecord>(
USR, Decl->getName(), createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Comment,
USR, Decl->getNameAsString(), createHierarchyInformationForDecl(*Decl),
Loc, AvailabilityInfo::createFromDecl(Decl), Comment,
DeclarationFragmentsBuilder::
getFragmentsForFunctionTemplateSpecialization(Decl),
SubHeading, Signature, Access, isInSystemHeader(Decl));
Expand All @@ -688,14 +691,14 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXMethodDecl(
SubHeading, Signature, Access, isInSystemHeader(Decl));
else if (Decl->isStatic())
API.createRecord<CXXStaticMethodRecord>(
USR, Decl->getName(), createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Comment,
USR, Decl->getNameAsString(), createHierarchyInformationForDecl(*Decl),
Loc, AvailabilityInfo::createFromDecl(Decl), Comment,
DeclarationFragmentsBuilder::getFragmentsForCXXMethod(Decl), SubHeading,
Signature, Access, isInSystemHeader(Decl));
else
API.createRecord<CXXInstanceMethodRecord>(
USR, Decl->getName(), createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Comment,
USR, Decl->getNameAsString(), createHierarchyInformationForDecl(*Decl),
Loc, AvailabilityInfo::createFromDecl(Decl), Comment,
DeclarationFragmentsBuilder::getFragmentsForCXXMethod(Decl), SubHeading,
Signature, Access, isInSystemHeader(Decl));

Expand Down Expand Up @@ -977,7 +980,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitFunctionTemplateDecl(
return true;

// Collect symbol information.
StringRef Name = Decl->getName();
auto Name = Decl->getNameAsString();
SmallString<128> USR;
index::generateUSRForDecl(Decl, USR);
PresumedLoc Loc =
Expand Down
7 changes: 4 additions & 3 deletions clang/include/clang/Lex/Preprocessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -2123,17 +2123,18 @@ class Preprocessor {
char
getSpellingOfSingleCharacterNumericConstant(const Token &Tok,
bool *Invalid = nullptr) const {
assert(Tok.is(tok::numeric_constant) &&
assert((Tok.is(tok::numeric_constant) || Tok.is(tok::binary_data)) &&
Tok.getLength() == 1 && "Called on unsupported token");
assert(!Tok.needsCleaning() && "Token can't need cleaning with length 1");

// If the token is carrying a literal data pointer, just use it.
if (const char *D = Tok.getLiteralData())
return *D;
return (Tok.getKind() == tok::binary_data) ? *D : *D - '0';

assert(Tok.is(tok::numeric_constant) && "binary data with no data");
// Otherwise, fall back on getCharacterData, which is slower, but always
// works.
return *SourceMgr.getCharacterData(Tok.getLocation(), Invalid);
return *SourceMgr.getCharacterData(Tok.getLocation(), Invalid) - '0';
}

/// Retrieve the name of the immediate macro expansion.
Expand Down
3 changes: 1 addition & 2 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -2127,7 +2127,7 @@ class Parser : public CodeCompletionHandler {
};
ExprResult ParseInitializerWithPotentialDesignator(DesignatorCompletionInfo);
ExprResult createEmbedExpr();
void ExpandEmbedDirective(SmallVectorImpl<Expr *> &Exprs);
void injectEmbedTokens();

//===--------------------------------------------------------------------===//
// clang Expressions
Expand Down Expand Up @@ -3834,7 +3834,6 @@ class Parser : public CodeCompletionHandler {
AnnotateTemplateIdTokenAsType(CXXScopeSpec &SS,
ImplicitTypenameContext AllowImplicitTypename,
bool IsClassName = false);
void ExpandEmbedIntoTemplateArgList(TemplateArgList &TemplateArgs);
bool ParseTemplateArgumentList(TemplateArgList &TemplateArgs,
TemplateTy Template, SourceLocation OpenLoc);
ParsedTemplateArgument ParseTemplateTemplateArgument();
Expand Down
1 change: 0 additions & 1 deletion clang/include/clang/Sema/HLSLExternalSemaSource.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ class Sema;
class HLSLExternalSemaSource : public ExternalSemaSource {
Sema *SemaPtr = nullptr;
NamespaceDecl *HLSLNamespace = nullptr;
CXXRecordDecl *ResourceDecl = nullptr;

using CompletionFunction = std::function<void(CXXRecordDecl *)>;
llvm::DenseMap<CXXRecordDecl *, CompletionFunction> Completions;
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -14078,6 +14078,11 @@ class Sema final : public SemaBase {
const DeclarationNameInfo &NameInfo,
SmallVectorImpl<UnexpandedParameterPack> &Unexpanded);

/// Collect the set of unexpanded parameter packs within the given
/// expression.
static void collectUnexpandedParameterPacks(
Expr *E, SmallVectorImpl<UnexpandedParameterPack> &Unexpanded);

/// Invoked when parsing a template argument followed by an
/// ellipsis, which creates a pack expansion.
///
Expand Down
193 changes: 160 additions & 33 deletions clang/include/clang/Sema/SemaConcept.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
namespace clang {
class Sema;

struct AtomicConstraint {
enum { ConstraintAlignment = 8 };

struct alignas(ConstraintAlignment) AtomicConstraint {
const Expr *ConstraintExpr;
std::optional<ArrayRef<TemplateArgumentLoc>> ParameterMapping;

Expand Down Expand Up @@ -75,6 +77,28 @@ struct AtomicConstraint {
}
};

struct alignas(ConstraintAlignment) FoldExpandedConstraint;

using NormalFormConstraint =
llvm::PointerUnion<AtomicConstraint *, FoldExpandedConstraint *>;
struct NormalizedConstraint;
using NormalForm =
llvm::SmallVector<llvm::SmallVector<NormalFormConstraint, 2>, 4>;

// A constraint is in conjunctive normal form when it is a conjunction of
// clauses where each clause is a disjunction of atomic constraints. For atomic
// constraints A, B, and C, the constraint A  ∧ (B  ∨ C) is in conjunctive
// normal form.
NormalForm makeCNF(const NormalizedConstraint &Normalized);

// A constraint is in disjunctive normal form when it is a disjunction of
// clauses where each clause is a conjunction of atomic constraints. For atomic
// constraints A, B, and C, the disjunctive normal form of the constraint A
//  ∧ (B  ∨ C) is (A  ∧ B)  ∨ (A  ∧ C).
NormalForm makeDNF(const NormalizedConstraint &Normalized);

struct alignas(ConstraintAlignment) NormalizedConstraintPair;

/// \brief A normalized constraint, as defined in C++ [temp.constr.normal], is
/// either an atomic constraint, a conjunction of normalized constraints or a
/// disjunction of normalized constraints.
Expand All @@ -83,30 +107,20 @@ struct NormalizedConstraint {

enum CompoundConstraintKind { CCK_Conjunction, CCK_Disjunction };

using CompoundConstraint = llvm::PointerIntPair<
std::pair<NormalizedConstraint, NormalizedConstraint> *, 1,
CompoundConstraintKind>;
using CompoundConstraint = llvm::PointerIntPair<NormalizedConstraintPair *, 1,
CompoundConstraintKind>;

llvm::PointerUnion<AtomicConstraint *, CompoundConstraint> Constraint;
llvm::PointerUnion<AtomicConstraint *, FoldExpandedConstraint *,
CompoundConstraint>
Constraint;

NormalizedConstraint(AtomicConstraint *C): Constraint{C} { };
NormalizedConstraint(FoldExpandedConstraint *C) : Constraint{C} {};

NormalizedConstraint(ASTContext &C, NormalizedConstraint LHS,
NormalizedConstraint RHS, CompoundConstraintKind Kind)
: Constraint{CompoundConstraint{
new (C) std::pair<NormalizedConstraint, NormalizedConstraint>{
std::move(LHS), std::move(RHS)}, Kind}} { };

NormalizedConstraint(ASTContext &C, const NormalizedConstraint &Other) {
if (Other.isAtomic()) {
Constraint = new (C) AtomicConstraint(*Other.getAtomicConstraint());
} else {
Constraint = CompoundConstraint(
new (C) std::pair<NormalizedConstraint, NormalizedConstraint>{
NormalizedConstraint(C, Other.getLHS()),
NormalizedConstraint(C, Other.getRHS())},
Other.getCompoundKind());
}
}
NormalizedConstraint RHS, CompoundConstraintKind Kind);

NormalizedConstraint(ASTContext &C, const NormalizedConstraint &Other);
NormalizedConstraint(NormalizedConstraint &&Other):
Constraint(Other.Constraint) {
Other.Constraint = nullptr;
Expand All @@ -120,36 +134,149 @@ struct NormalizedConstraint {
return *this;
}

CompoundConstraintKind getCompoundKind() const {
assert(!isAtomic() && "getCompoundKind called on atomic constraint.");
return Constraint.get<CompoundConstraint>().getInt();
}

bool isAtomic() const { return Constraint.is<AtomicConstraint *>(); }

NormalizedConstraint &getLHS() const {
assert(!isAtomic() && "getLHS called on atomic constraint.");
return Constraint.get<CompoundConstraint>().getPointer()->first;
bool isFoldExpanded() const {
return Constraint.is<FoldExpandedConstraint *>();
}
bool isCompound() const { return Constraint.is<CompoundConstraint>(); }

NormalizedConstraint &getRHS() const {
assert(!isAtomic() && "getRHS called on atomic constraint.");
return Constraint.get<CompoundConstraint>().getPointer()->second;
CompoundConstraintKind getCompoundKind() const {
assert(isCompound() && "getCompoundKind on a non-compound constraint..");
return Constraint.get<CompoundConstraint>().getInt();
}

NormalizedConstraint &getLHS() const;
NormalizedConstraint &getRHS() const;

AtomicConstraint *getAtomicConstraint() const {
assert(isAtomic() &&
"getAtomicConstraint called on non-atomic constraint.");
return Constraint.get<AtomicConstraint *>();
}

FoldExpandedConstraint *getFoldExpandedConstraint() const {
assert(isFoldExpanded() &&
"getFoldExpandedConstraint called on non-fold-expanded constraint.");
return Constraint.get<FoldExpandedConstraint *>();
}

private:
static std::optional<NormalizedConstraint>
fromConstraintExprs(Sema &S, NamedDecl *D, ArrayRef<const Expr *> E);
static std::optional<NormalizedConstraint>
fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E);
};

struct alignas(ConstraintAlignment) NormalizedConstraintPair {
NormalizedConstraint LHS, RHS;
};

struct alignas(ConstraintAlignment) FoldExpandedConstraint {
enum class FoldOperatorKind { And, Or } Kind;
NormalizedConstraint Constraint;
const Expr *Pattern;

FoldExpandedConstraint(FoldOperatorKind K, NormalizedConstraint C,
const Expr *Pattern)
: Kind(K), Constraint(std::move(C)), Pattern(Pattern) {};

template <typename AtomicSubsumptionEvaluator>
bool subsumes(const FoldExpandedConstraint &Other,
const AtomicSubsumptionEvaluator &E) const;

static bool AreCompatibleForSubsumption(const FoldExpandedConstraint &A,
const FoldExpandedConstraint &B);
};

const NormalizedConstraint *getNormalizedAssociatedConstraints(
Sema &S, NamedDecl *ConstrainedDecl,
ArrayRef<const Expr *> AssociatedConstraints);

template <typename AtomicSubsumptionEvaluator>
bool subsumes(const NormalForm &PDNF, const NormalForm &QCNF,
const AtomicSubsumptionEvaluator &E) {
// C++ [temp.constr.order] p2
// Then, P subsumes Q if and only if, for every disjunctive clause Pi in the
// disjunctive normal form of P, Pi subsumes every conjunctive clause Qj in
// the conjuctive normal form of Q, where [...]
for (const auto &Pi : PDNF) {
for (const auto &Qj : QCNF) {
// C++ [temp.constr.order] p2
// - [...] a disjunctive clause Pi subsumes a conjunctive clause Qj if
// and only if there exists an atomic constraint Pia in Pi for which
// there exists an atomic constraint, Qjb, in Qj such that Pia
// subsumes Qjb.
bool Found = false;
for (NormalFormConstraint Pia : Pi) {
for (NormalFormConstraint Qjb : Qj) {
if (Pia.is<FoldExpandedConstraint *>() &&
Qjb.is<FoldExpandedConstraint *>()) {
if (Pia.get<FoldExpandedConstraint *>()->subsumes(
*Qjb.get<FoldExpandedConstraint *>(), E)) {
Found = true;
break;
}
} else if (Pia.is<AtomicConstraint *>() &&
Qjb.is<AtomicConstraint *>()) {
if (E(*Pia.get<AtomicConstraint *>(),
*Qjb.get<AtomicConstraint *>())) {
Found = true;
break;
}
}
}
if (Found)
break;
}
if (!Found)
return false;
}
}
return true;
}

template <typename AtomicSubsumptionEvaluator>
bool subsumes(Sema &S, NamedDecl *DP, ArrayRef<const Expr *> P, NamedDecl *DQ,
ArrayRef<const Expr *> Q, bool &Subsumes,
const AtomicSubsumptionEvaluator &E) {
// C++ [temp.constr.order] p2
// In order to determine if a constraint P subsumes a constraint Q, P is
// transformed into disjunctive normal form, and Q is transformed into
// conjunctive normal form. [...]
const NormalizedConstraint *PNormalized =
getNormalizedAssociatedConstraints(S, DP, P);
if (!PNormalized)
return true;
NormalForm PDNF = makeDNF(*PNormalized);

const NormalizedConstraint *QNormalized =
getNormalizedAssociatedConstraints(S, DQ, Q);
if (!QNormalized)
return true;
NormalForm QCNF = makeCNF(*QNormalized);

Subsumes = subsumes(PDNF, QCNF, E);
return false;
}

template <typename AtomicSubsumptionEvaluator>
bool FoldExpandedConstraint::subsumes(
const FoldExpandedConstraint &Other,
const AtomicSubsumptionEvaluator &E) const {

// [C++26] [temp.constr.order]
// a fold expanded constraint A subsumes another fold expanded constraint B if
// they are compatible for subsumption, have the same fold-operator, and the
// constraint of A subsumes that of B

if (Kind != Other.Kind || !AreCompatibleForSubsumption(*this, Other))
return false;

NormalForm PDNF = makeDNF(this->Constraint);
NormalForm QCNF = makeCNF(Other.Constraint);
return clang::subsumes(PDNF, QCNF, E);
}

} // clang

#endif // LLVM_CLANG_SEMA_SEMACONCEPT_H
3 changes: 3 additions & 0 deletions clang/include/clang/Sema/SemaOpenMP.h
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,9 @@ class SemaOpenMP : public SemaBase {
StmtResult ActOnOpenMPUnrollDirective(ArrayRef<OMPClause *> Clauses,
Stmt *AStmt, SourceLocation StartLoc,
SourceLocation EndLoc);
/// Called on well-formed '#pragma omp reverse'.
StmtResult ActOnOpenMPReverseDirective(Stmt *AStmt, SourceLocation StartLoc,
SourceLocation EndLoc);
/// Called on well-formed '\#pragma omp for' after parsing
/// of the associated statement.
StmtResult
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Serialization/ASTBitCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1895,6 +1895,7 @@ enum StmtCode {
STMT_OMP_SIMD_DIRECTIVE,
STMT_OMP_TILE_DIRECTIVE,
STMT_OMP_UNROLL_DIRECTIVE,
STMT_OMP_REVERSE_DIRECTIVE,
STMT_OMP_FOR_DIRECTIVE,
STMT_OMP_FOR_SIMD_DIRECTIVE,
STMT_OMP_SECTIONS_DIRECTIVE,
Expand Down
57 changes: 50 additions & 7 deletions clang/lib/APINotes/APINotesFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const uint16_t VERSION_MAJOR = 0;
/// API notes file minor version number.
///
/// When the format changes IN ANY WAY, this number should be incremented.
const uint16_t VERSION_MINOR = 26; // SwiftCopyable
const uint16_t VERSION_MINOR = 27; // SingleDeclTableKey

const uint8_t kSwiftCopyable = 1;
const uint8_t kSwiftNonCopyable = 2;
Expand Down Expand Up @@ -269,12 +269,6 @@ struct ContextTableKey {
: parentContextID(parentContextID), contextKind(contextKind),
contextID(contextID) {}

ContextTableKey(std::optional<Context> context, IdentifierID nameID)
: parentContextID(context ? context->id.Value : (uint32_t)-1),
contextKind(context ? static_cast<uint8_t>(context->kind)
: static_cast<uint8_t>(-1)),
contextID(nameID) {}

llvm::hash_code hashValue() const {
return llvm::hash_value(
std::tuple{parentContextID, contextKind, contextID});
Expand All @@ -286,6 +280,32 @@ inline bool operator==(const ContextTableKey &lhs, const ContextTableKey &rhs) {
lhs.contextKind == rhs.contextKind && lhs.contextID == rhs.contextID;
}

/// A stored Objective-C or C++ declaration, represented by the ID of its parent
/// context, and the name of the declaration.
struct SingleDeclTableKey {
uint32_t parentContextID;
uint32_t nameID;

SingleDeclTableKey() : parentContextID(-1), nameID(-1) {}

SingleDeclTableKey(uint32_t ParentContextID, uint32_t NameID)
: parentContextID(ParentContextID), nameID(NameID) {}

SingleDeclTableKey(std::optional<Context> ParentCtx, IdentifierID NameID)
: parentContextID(ParentCtx ? ParentCtx->id.Value
: static_cast<uint32_t>(-1)),
nameID(NameID) {}

llvm::hash_code hashValue() const {
return llvm::hash_value(std::make_pair(parentContextID, nameID));
}
};

inline bool operator==(const SingleDeclTableKey &lhs,
const SingleDeclTableKey &rhs) {
return lhs.parentContextID == rhs.parentContextID && lhs.nameID == rhs.nameID;
}

} // namespace api_notes
} // namespace clang

Expand Down Expand Up @@ -341,6 +361,29 @@ template <> struct DenseMapInfo<clang::api_notes::ContextTableKey> {
return lhs == rhs;
}
};

template <> struct DenseMapInfo<clang::api_notes::SingleDeclTableKey> {
static inline clang::api_notes::SingleDeclTableKey getEmptyKey() {
return clang::api_notes::SingleDeclTableKey();
}

static inline clang::api_notes::SingleDeclTableKey getTombstoneKey() {
return clang::api_notes::SingleDeclTableKey{
DenseMapInfo<uint32_t>::getTombstoneKey(),
DenseMapInfo<uint32_t>::getTombstoneKey()};
}

static unsigned
getHashValue(const clang::api_notes::SingleDeclTableKey &value) {
return value.hashValue();
}

static bool isEqual(const clang::api_notes::SingleDeclTableKey &lhs,
const clang::api_notes::SingleDeclTableKey &rhs) {
return lhs == rhs;
}
};

} // namespace llvm

#endif
32 changes: 12 additions & 20 deletions clang/lib/APINotes/APINotesReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -429,15 +429,13 @@ class ObjCSelectorTableInfo {

/// Used to deserialize the on-disk global variable table.
class GlobalVariableTableInfo
: public VersionedTableInfo<GlobalVariableTableInfo, ContextTableKey,
: public VersionedTableInfo<GlobalVariableTableInfo, SingleDeclTableKey,
GlobalVariableInfo> {
public:
static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
auto CtxID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
auto ContextKind =
endian::readNext<uint8_t, llvm::endianness::little>(Data);
auto NameID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
return {CtxID, ContextKind, NameID};
return {CtxID, NameID};
}

hash_value_type ComputeHash(internal_key_type Key) {
Expand All @@ -454,15 +452,13 @@ class GlobalVariableTableInfo

/// Used to deserialize the on-disk global function table.
class GlobalFunctionTableInfo
: public VersionedTableInfo<GlobalFunctionTableInfo, ContextTableKey,
: public VersionedTableInfo<GlobalFunctionTableInfo, SingleDeclTableKey,
GlobalFunctionInfo> {
public:
static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
auto CtxID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
auto ContextKind =
endian::readNext<uint8_t, llvm::endianness::little>(Data);
auto NameID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
return {CtxID, ContextKind, NameID};
return {CtxID, NameID};
}

hash_value_type ComputeHash(internal_key_type Key) {
Expand Down Expand Up @@ -501,15 +497,13 @@ class EnumConstantTableInfo

/// Used to deserialize the on-disk tag table.
class TagTableInfo
: public VersionedTableInfo<TagTableInfo, ContextTableKey, TagInfo> {
: public VersionedTableInfo<TagTableInfo, SingleDeclTableKey, TagInfo> {
public:
static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
auto CtxID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
auto ContextKind =
endian::readNext<uint8_t, llvm::endianness::little>(Data);
auto NameID =
endian::readNext<IdentifierID, llvm::endianness::little>(Data);
return {CtxID, ContextKind, NameID};
return {CtxID, NameID};
}

hash_value_type ComputeHash(internal_key_type Key) {
Expand Down Expand Up @@ -563,16 +557,14 @@ class TagTableInfo

/// Used to deserialize the on-disk typedef table.
class TypedefTableInfo
: public VersionedTableInfo<TypedefTableInfo, ContextTableKey,
: public VersionedTableInfo<TypedefTableInfo, SingleDeclTableKey,
TypedefInfo> {
public:
static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
auto CtxID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
auto ContextKind =
endian::readNext<uint8_t, llvm::endianness::little>(Data);
auto nameID =
endian::readNext<IdentifierID, llvm::endianness::little>(Data);
return {CtxID, ContextKind, nameID};
return {CtxID, nameID};
}

hash_value_type ComputeHash(internal_key_type Key) {
Expand Down Expand Up @@ -1929,7 +1921,7 @@ auto APINotesReader::lookupGlobalVariable(llvm::StringRef Name,
if (!NameID)
return std::nullopt;

ContextTableKey Key(Ctx, *NameID);
SingleDeclTableKey Key(Ctx, *NameID);

auto Known = Implementation->GlobalVariableTable->find(Key);
if (Known == Implementation->GlobalVariableTable->end())
Expand All @@ -1948,7 +1940,7 @@ auto APINotesReader::lookupGlobalFunction(llvm::StringRef Name,
if (!NameID)
return std::nullopt;

ContextTableKey Key(Ctx, *NameID);
SingleDeclTableKey Key(Ctx, *NameID);

auto Known = Implementation->GlobalFunctionTable->find(Key);
if (Known == Implementation->GlobalFunctionTable->end())
Expand Down Expand Up @@ -1982,7 +1974,7 @@ auto APINotesReader::lookupTag(llvm::StringRef Name, std::optional<Context> Ctx)
if (!NameID)
return std::nullopt;

ContextTableKey Key(Ctx, *NameID);
SingleDeclTableKey Key(Ctx, *NameID);

auto Known = Implementation->TagTable->find(Key);
if (Known == Implementation->TagTable->end())
Expand All @@ -2001,7 +1993,7 @@ auto APINotesReader::lookupTypedef(llvm::StringRef Name,
if (!NameID)
return std::nullopt;

ContextTableKey Key(Ctx, *NameID);
SingleDeclTableKey Key(Ctx, *NameID);

auto Known = Implementation->TypedefTable->find(Key);
if (Known == Implementation->TypedefTable->end())
Expand Down
46 changes: 22 additions & 24 deletions clang/lib/APINotes/APINotesWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,17 +75,17 @@ class APINotesWriter::Implementation {

/// Information about global variables.
///
/// Indexed by the context ID, contextKind, identifier ID.
/// Indexed by the context ID, identifier ID.
llvm::DenseMap<
ContextTableKey,
SingleDeclTableKey,
llvm::SmallVector<std::pair<VersionTuple, GlobalVariableInfo>, 1>>
GlobalVariables;

/// Information about global functions.
///
/// Indexed by the context ID, contextKind, identifier ID.
/// Indexed by the context ID, identifier ID.
llvm::DenseMap<
ContextTableKey,
SingleDeclTableKey,
llvm::SmallVector<std::pair<VersionTuple, GlobalFunctionInfo>, 1>>
GlobalFunctions;

Expand All @@ -98,15 +98,15 @@ class APINotesWriter::Implementation {

/// Information about tags.
///
/// Indexed by the context ID, contextKind, identifier ID.
llvm::DenseMap<ContextTableKey,
/// Indexed by the context ID, identifier ID.
llvm::DenseMap<SingleDeclTableKey,
llvm::SmallVector<std::pair<VersionTuple, TagInfo>, 1>>
Tags;

/// Information about typedefs.
///
/// Indexed by the context ID, contextKind, identifier ID.
llvm::DenseMap<ContextTableKey,
/// Indexed by the context ID, identifier ID.
llvm::DenseMap<SingleDeclTableKey,
llvm::SmallVector<std::pair<VersionTuple, TypedefInfo>, 1>>
Typedefs;

Expand Down Expand Up @@ -865,18 +865,17 @@ void APINotesWriter::Implementation::writeObjCSelectorBlock(
namespace {
/// Used to serialize the on-disk global variable table.
class GlobalVariableTableInfo
: public VersionedTableInfo<GlobalVariableTableInfo, ContextTableKey,
: public VersionedTableInfo<GlobalVariableTableInfo, SingleDeclTableKey,
GlobalVariableInfo> {
public:
unsigned getKeyLength(key_type_ref) {
return sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint32_t);
return sizeof(uint32_t) + sizeof(uint32_t);
}

void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) {
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
writer.write<uint32_t>(Key.parentContextID);
writer.write<uint8_t>(Key.contextKind);
writer.write<uint32_t>(Key.contextID);
writer.write<uint32_t>(Key.nameID);
}

hash_value_type ComputeHash(key_type_ref Key) {
Expand Down Expand Up @@ -979,18 +978,17 @@ void emitFunctionInfo(raw_ostream &OS, const FunctionInfo &FI) {

/// Used to serialize the on-disk global function table.
class GlobalFunctionTableInfo
: public VersionedTableInfo<GlobalFunctionTableInfo, ContextTableKey,
: public VersionedTableInfo<GlobalFunctionTableInfo, SingleDeclTableKey,
GlobalFunctionInfo> {
public:
unsigned getKeyLength(key_type_ref) {
return sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint32_t);
return sizeof(uint32_t) + sizeof(uint32_t);
}

void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) {
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
writer.write<uint32_t>(Key.parentContextID);
writer.write<uint8_t>(Key.contextKind);
writer.write<uint32_t>(Key.contextID);
writer.write<uint32_t>(Key.nameID);
}

hash_value_type ComputeHash(key_type_ref Key) {
Expand Down Expand Up @@ -1091,20 +1089,20 @@ void APINotesWriter::Implementation::writeEnumConstantBlock(
namespace {
template <typename Derived, typename UnversionedDataType>
class CommonTypeTableInfo
: public VersionedTableInfo<Derived, ContextTableKey, UnversionedDataType> {
: public VersionedTableInfo<Derived, SingleDeclTableKey,
UnversionedDataType> {
public:
using key_type_ref = typename CommonTypeTableInfo::key_type_ref;
using hash_value_type = typename CommonTypeTableInfo::hash_value_type;

unsigned getKeyLength(key_type_ref) {
return sizeof(uint32_t) + sizeof(uint8_t) + sizeof(IdentifierID);
return sizeof(uint32_t) + sizeof(IdentifierID);
}

void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) {
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
writer.write<uint32_t>(Key.parentContextID);
writer.write<uint8_t>(Key.contextKind);
writer.write<IdentifierID>(Key.contextID);
writer.write<IdentifierID>(Key.nameID);
}

hash_value_type ComputeHash(key_type_ref Key) {
Expand Down Expand Up @@ -1351,7 +1349,7 @@ void APINotesWriter::addGlobalVariable(std::optional<Context> Ctx,
const GlobalVariableInfo &Info,
VersionTuple SwiftVersion) {
IdentifierID VariableID = Implementation->getIdentifier(Name);
ContextTableKey Key(Ctx, VariableID);
SingleDeclTableKey Key(Ctx, VariableID);
Implementation->GlobalVariables[Key].push_back({SwiftVersion, Info});
}

Expand All @@ -1360,7 +1358,7 @@ void APINotesWriter::addGlobalFunction(std::optional<Context> Ctx,
const GlobalFunctionInfo &Info,
VersionTuple SwiftVersion) {
IdentifierID NameID = Implementation->getIdentifier(Name);
ContextTableKey Key(Ctx, NameID);
SingleDeclTableKey Key(Ctx, NameID);
Implementation->GlobalFunctions[Key].push_back({SwiftVersion, Info});
}

Expand All @@ -1374,15 +1372,15 @@ void APINotesWriter::addEnumConstant(llvm::StringRef Name,
void APINotesWriter::addTag(std::optional<Context> Ctx, llvm::StringRef Name,
const TagInfo &Info, VersionTuple SwiftVersion) {
IdentifierID TagID = Implementation->getIdentifier(Name);
ContextTableKey Key(Ctx, TagID);
SingleDeclTableKey Key(Ctx, TagID);
Implementation->Tags[Key].push_back({SwiftVersion, Info});
}

void APINotesWriter::addTypedef(std::optional<Context> Ctx,
llvm::StringRef Name, const TypedefInfo &Info,
VersionTuple SwiftVersion) {
IdentifierID TypedefID = Implementation->getIdentifier(Name);
ContextTableKey Key(Ctx, TypedefID);
SingleDeclTableKey Key(Ctx, TypedefID);
Implementation->Typedefs[Key].push_back({SwiftVersion, Info});
}
} // namespace api_notes
Expand Down
28 changes: 17 additions & 11 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2831,6 +2831,10 @@ bool ASTContext::hasUniqueObjectRepresentations(
return hasUniqueObjectRepresentations(getBaseElementType(Ty),
CheckIfTriviallyCopyable);

assert((Ty->isVoidType() || !Ty->isIncompleteType()) &&
"hasUniqueObjectRepresentations should not be called with an "
"incomplete type");

// (9.1) - T is trivially copyable...
if (CheckIfTriviallyCopyable && !Ty.isTriviallyCopyableType(*this))
return false;
Expand Down Expand Up @@ -4892,21 +4896,23 @@ QualType ASTContext::getFunctionTypeInternal(
size_t Size = FunctionProtoType::totalSizeToAlloc<
QualType, SourceLocation, FunctionType::FunctionTypeExtraBitfields,
FunctionType::FunctionTypeArmAttributes, FunctionType::ExceptionType,
Expr *, FunctionDecl *, FunctionProtoType::ExtParameterInfo,
FunctionEffect, EffectConditionExpr, Qualifiers>(
Expr *, FunctionDecl *, FunctionProtoType::ExtParameterInfo, Qualifiers,
FunctionEffect, EffectConditionExpr>(
NumArgs, EPI.Variadic, EPI.requiresFunctionProtoTypeExtraBitfields(),
EPI.requiresFunctionProtoTypeArmAttributes(), ESH.NumExceptionType,
ESH.NumExprPtr, ESH.NumFunctionDeclPtr,
EPI.ExtParameterInfos ? NumArgs : 0, EPI.FunctionEffects.size(),
EPI.FunctionEffects.conditions().size(),
EPI.TypeQuals.hasNonFastQualifiers() ? 1 : 0);
EPI.ExtParameterInfos ? NumArgs : 0,
EPI.TypeQuals.hasNonFastQualifiers() ? 1 : 0, EPI.FunctionEffects.size(),
EPI.FunctionEffects.conditions().size());

auto *FTP = (FunctionProtoType *)Allocate(Size, alignof(FunctionProtoType));
FunctionProtoType::ExtProtoInfo newEPI = EPI;
new (FTP) FunctionProtoType(ResultTy, ArgArray, Canonical, newEPI);
Types.push_back(FTP);
if (!Unique)
FunctionProtoTypes.InsertNode(FTP, InsertPos);
if (!EPI.FunctionEffects.empty())
AnyFunctionEffects = true;
return QualType(FTP, 0);
}

Expand Down Expand Up @@ -6016,19 +6022,19 @@ QualType ASTContext::getTypeOfExprType(Expr *tofExpr, TypeOfKind Kind) const {
if (Canon) {
// We already have a "canonical" version of an identical, dependent
// typeof(expr) type. Use that as our canonical type.
toe = new (*this, alignof(TypeOfExprType))
TypeOfExprType(tofExpr, Kind, QualType((TypeOfExprType *)Canon, 0));
toe = new (*this, alignof(TypeOfExprType)) TypeOfExprType(
*this, tofExpr, Kind, QualType((TypeOfExprType *)Canon, 0));
} else {
// Build a new, canonical typeof(expr) type.
Canon = new (*this, alignof(DependentTypeOfExprType))
DependentTypeOfExprType(tofExpr, Kind);
DependentTypeOfExprType(*this, tofExpr, Kind);
DependentTypeOfExprTypes.InsertNode(Canon, InsertPos);
toe = Canon;
}
} else {
QualType Canonical = getCanonicalType(tofExpr->getType());
toe = new (*this, alignof(TypeOfExprType))
TypeOfExprType(tofExpr, Kind, Canonical);
TypeOfExprType(*this, tofExpr, Kind, Canonical);
}
Types.push_back(toe);
return QualType(toe, 0);
Expand All @@ -6041,8 +6047,8 @@ QualType ASTContext::getTypeOfExprType(Expr *tofExpr, TypeOfKind Kind) const {
/// on canonical types (which are always unique).
QualType ASTContext::getTypeOfType(QualType tofType, TypeOfKind Kind) const {
QualType Canonical = getCanonicalType(tofType);
auto *tot =
new (*this, alignof(TypeOfType)) TypeOfType(tofType, Canonical, Kind);
auto *tot = new (*this, alignof(TypeOfType))
TypeOfType(*this, tofType, Canonical, Kind);
Types.push_back(tot);
return QualType(tot, 0);
}
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/ASTImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1551,7 +1551,7 @@ ASTNodeImporter::VisitCountAttributedType(const CountAttributedType *T) {
Expr *CountExpr = importChecked(Err, T->getCountExpr());

SmallVector<TypeCoupledDeclRefInfo, 1> CoupledDecls;
for (auto TI : T->dependent_decls()) {
for (const TypeCoupledDeclRefInfo &TI : T->dependent_decls()) {
Expected<ValueDecl *> ToDeclOrErr = import(TI.getDecl());
if (!ToDeclOrErr)
return ToDeclOrErr.takeError();
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ add_clang_library(clangAST
Interp/InterpBuiltin.cpp
Interp/Floating.cpp
Interp/EvaluationResult.cpp
Interp/DynamicAllocator.cpp
Interp/Interp.cpp
Interp/InterpBlock.cpp
Interp/InterpFrame.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11478,7 +11478,7 @@ bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E,

bool ArrayExprEvaluator::VisitCXXParenListInitExpr(
const CXXParenListInitExpr *E) {
assert(dyn_cast<ConstantArrayType>(E->getType()) &&
assert(E->getType()->isConstantArrayType() &&
"Expression result is not a constant array type");

return VisitCXXParenListOrInitListExpr(E, E->getInitExprs(),
Expand Down
11 changes: 11 additions & 0 deletions clang/lib/AST/Interp/ByteCodeEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ static bool isUnevaluatedBuiltin(unsigned BuiltinID) {
}

Function *ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {

// Manually created functions that haven't been assigned proper
// parameters yet.
if (!FuncDecl->param_empty() && !FuncDecl->param_begin())
return nullptr;

bool IsLambdaStaticInvoker = false;
if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl);
MD && MD->isLambdaStaticInvoker()) {
Expand Down Expand Up @@ -93,6 +99,11 @@ Function *ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {

// Set up lambda capture to closure record field mapping.
if (isLambdaCallOperator(MD)) {
// The parent record needs to be complete, we need to know about all
// the lambda captures.
if (!MD->getParent()->isCompleteDefinition())
return nullptr;

const Record *R = P.getOrCreateRecord(MD->getParent());
llvm::DenseMap<const ValueDecl *, FieldDecl *> LC;
FieldDecl *LTC;
Expand Down
111 changes: 111 additions & 0 deletions clang/lib/AST/Interp/Compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2771,6 +2771,117 @@ bool Compiler<Emitter>::VisitCXXInheritedCtorInitExpr(
return this->emitCall(F, 0, E);
}

template <class Emitter>
bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) {
assert(classifyPrim(E->getType()) == PT_Ptr);
const Expr *Init = E->getInitializer();
QualType ElementType = E->getAllocatedType();
std::optional<PrimType> ElemT = classify(ElementType);
unsigned PlacementArgs = E->getNumPlacementArgs();
bool IsNoThrow = false;

// FIXME: Better diagnostic. diag::note_constexpr_new_placement
if (PlacementArgs != 0) {
// The only new-placement list we support is of the form (std::nothrow).
//
// FIXME: There is no restriction on this, but it's not clear that any
// other form makes any sense. We get here for cases such as:
//
// new (std::align_val_t{N}) X(int)
//
// (which should presumably be valid only if N is a multiple of
// alignof(int), and in any case can't be deallocated unless N is
// alignof(X) and X has new-extended alignment).
if (PlacementArgs != 1 || !E->getPlacementArg(0)->getType()->isNothrowT())
return this->emitInvalid(E);

if (!this->discard(E->getPlacementArg(0)))
return false;
IsNoThrow = true;
}

const Descriptor *Desc;
if (ElemT) {
if (E->isArray())
Desc = nullptr; // We're not going to use it in this case.
else
Desc = P.createDescriptor(E, *ElemT, Descriptor::InlineDescMD,
/*IsConst=*/false, /*IsTemporary=*/false,
/*IsMutable=*/false);
} else {
Desc = P.createDescriptor(
E, ElementType.getTypePtr(),
E->isArray() ? std::nullopt : Descriptor::InlineDescMD,
/*IsConst=*/false, /*IsTemporary=*/false, /*IsMutable=*/false, Init);
}

if (E->isArray()) {
std::optional<const Expr *> ArraySizeExpr = E->getArraySize();
if (!ArraySizeExpr)
return false;

const Expr *Stripped = *ArraySizeExpr;
for (; auto *ICE = dyn_cast<ImplicitCastExpr>(Stripped);
Stripped = ICE->getSubExpr())
if (ICE->getCastKind() != CK_NoOp &&
ICE->getCastKind() != CK_IntegralCast)
break;

PrimType SizeT = classifyPrim(Stripped->getType());

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

if (ElemT) {
// N primitive elements.
if (!this->emitAllocN(SizeT, *ElemT, E, IsNoThrow, E))
return false;
} else {
// N Composite elements.
if (!this->emitAllocCN(SizeT, Desc, IsNoThrow, E))
return false;
}

if (Init && !this->visitInitializer(Init))
return false;

} else {
// Allocate just one element.
if (!this->emitAlloc(Desc, E))
return false;

if (Init) {
if (ElemT) {
if (!this->visit(Init))
return false;

if (!this->emitInit(*ElemT, E))
return false;
} else {
// Composite.
if (!this->visitInitializer(Init))
return false;
}
}
}

if (DiscardResult)
return this->emitPopPtr(E);

return true;
}

template <class Emitter>
bool Compiler<Emitter>::VisitCXXDeleteExpr(const CXXDeleteExpr *E) {
const Expr *Arg = E->getArgument();

// Arg must be an lvalue.
if (!this->visit(Arg))
return false;

return this->emitFree(E->isArrayForm(), E);
}

template <class Emitter>
bool Compiler<Emitter>::VisitExpressionTraitExpr(const ExpressionTraitExpr *E) {
assert(Ctx.getLangOpts().CPlusPlus);
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/Interp/Compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
bool VisitObjCBoxedExpr(const ObjCBoxedExpr *E);
bool VisitCXXStdInitializerListExpr(const CXXStdInitializerListExpr *E);
bool VisitStmtExpr(const StmtExpr *E);
bool VisitCXXNewExpr(const CXXNewExpr *E);
bool VisitCXXDeleteExpr(const CXXDeleteExpr *E);

// Statements.
bool visitCompoundStmt(const CompoundStmt *S);
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/Interp/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) {
if (!Func || !Func->hasBody())
Func = Compiler<ByteCodeEmitter>(*this, *P).compileFunc(FD);

if (!Func)
return false;

APValue DummyResult;
if (!Run(Parent, Func, DummyResult))
return false;
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/Descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
IsArray(true), CtorFn(getCtorArrayPrim(Type)),
DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) {
assert(Source && "Missing source");
assert(NumElems <= (MaxArrayElemBytes / ElemSize));
}

/// Primitive unknown-size arrays.
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/Interp/Descriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#ifndef LLVM_CLANG_AST_INTERP_DESCRIPTOR_H
#define LLVM_CLANG_AST_INTERP_DESCRIPTOR_H

#include "PrimType.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"

Expand Down Expand Up @@ -125,6 +126,11 @@ struct Descriptor final {
static constexpr MetadataSize InlineDescMD = sizeof(InlineDescriptor);
static constexpr MetadataSize GlobalMD = sizeof(GlobalInlineDescriptor);

/// Maximum number of bytes to be used for array elements.
static constexpr unsigned MaxArrayElemBytes =
std::numeric_limits<decltype(AllocSize)>::max() - sizeof(InitMapPtr) -
align(std::max(*InlineDescMD, *GlobalMD));

/// Pointer to the record, if block contains records.
const Record *const ElemRecord = nullptr;
/// Descriptor of the array element.
Expand Down
118 changes: 118 additions & 0 deletions clang/lib/AST/Interp/DynamicAllocator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
//==-------- DynamicAllocator.cpp - Dynamic allocations ----------*- C++ -*-==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "DynamicAllocator.h"
#include "InterpBlock.h"
#include "InterpState.h"

using namespace clang;
using namespace clang::interp;

DynamicAllocator::~DynamicAllocator() { cleanup(); }

void DynamicAllocator::cleanup() {
// Invoke destructors of all the blocks and as a last restort,
// reset all the pointers pointing to them to null pointees.
// This should never show up in diagnostics, but it's necessary
// for us to not cause use-after-free problems.
for (auto &Iter : AllocationSites) {
auto &AllocSite = Iter.second;
for (auto &Alloc : AllocSite.Allocations) {
Block *B = reinterpret_cast<Block *>(Alloc.Memory.get());
B->invokeDtor();
if (B->hasPointers()) {
while (B->Pointers) {
Pointer *Next = B->Pointers->Next;
B->Pointers->PointeeStorage.BS.Pointee = nullptr;
B->Pointers = Next;
}
B->Pointers = nullptr;
}
}
}

AllocationSites.clear();
}

Block *DynamicAllocator::allocate(const Expr *Source, PrimType T,
size_t NumElements, unsigned EvalID) {
// Create a new descriptor for an array of the specified size and
// element type.
const Descriptor *D = allocateDescriptor(
Source, T, Descriptor::InlineDescMD, NumElements, /*IsConst=*/false,
/*IsTemporary=*/false, /*IsMutable=*/false);

return allocate(D, EvalID);
}

Block *DynamicAllocator::allocate(const Descriptor *ElementDesc,
size_t NumElements, unsigned EvalID) {
// Create a new descriptor for an array of the specified size and
// element type.
const Descriptor *D = allocateDescriptor(
ElementDesc->asExpr(), ElementDesc, Descriptor::InlineDescMD, NumElements,
/*IsConst=*/false, /*IsTemporary=*/false, /*IsMutable=*/false);
return allocate(D, EvalID);
}

Block *DynamicAllocator::allocate(const Descriptor *D, unsigned EvalID) {
assert(D);
assert(D->asExpr());

auto Memory =
std::make_unique<std::byte[]>(sizeof(Block) + D->getAllocSize());
auto *B = new (Memory.get()) Block(EvalID, D, /*isStatic=*/false);
B->invokeCtor();

InlineDescriptor *ID = reinterpret_cast<InlineDescriptor *>(B->rawData());
ID->Desc = D;
ID->IsActive = true;
ID->Offset = sizeof(InlineDescriptor);
ID->IsBase = false;
ID->IsFieldMutable = false;
ID->IsConst = false;
ID->IsInitialized = false;

B->IsDynamic = true;

if (auto It = AllocationSites.find(D->asExpr()); It != AllocationSites.end())
It->second.Allocations.emplace_back(std::move(Memory));
else
AllocationSites.insert(
{D->asExpr(), AllocationSite(std::move(Memory), D->isArray())});
return B;
}

bool DynamicAllocator::deallocate(const Expr *Source,
const Block *BlockToDelete, InterpState &S) {
auto It = AllocationSites.find(Source);
if (It == AllocationSites.end())
return false;

auto &Site = It->second;
assert(Site.size() > 0);

// Find the Block to delete.
auto AllocIt = llvm::find_if(Site.Allocations, [&](const Allocation &A) {
const Block *B = reinterpret_cast<const Block *>(A.Memory.get());
return BlockToDelete == B;
});

assert(AllocIt != Site.Allocations.end());

Block *B = reinterpret_cast<Block *>(AllocIt->Memory.get());
B->invokeDtor();

S.deallocate(B);
Site.Allocations.erase(AllocIt);

if (Site.size() == 0)
AllocationSites.erase(It);

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

#ifndef LLVM_CLANG_AST_INTERP_DYNAMIC_ALLOCATOR_H
#define LLVM_CLANG_AST_INTERP_DYNAMIC_ALLOCATOR_H

#include "Descriptor.h"
#include "InterpBlock.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/Allocator.h"

namespace clang {
class Expr;
namespace interp {
class Block;
class InterpState;

/// Manages dynamic memory allocations done during bytecode interpretation.
///
/// We manage allocations as a map from their new-expression to a list
/// of allocations. This is called an AllocationSite. For each site, we
/// record whether it was allocated using new or new[], the
/// IsArrayAllocation flag.
///
/// For all array allocations, we need to allocate new Descriptor instances,
/// so the DynamicAllocator has a llvm::BumpPtrAllocator similar to Program.
class DynamicAllocator final {
struct Allocation {
std::unique_ptr<std::byte[]> Memory;
Allocation(std::unique_ptr<std::byte[]> Memory)
: Memory(std::move(Memory)) {}
};

struct AllocationSite {
llvm::SmallVector<Allocation> Allocations;
bool IsArrayAllocation = false;

AllocationSite(std::unique_ptr<std::byte[]> Memory, bool Array)
: IsArrayAllocation(Array) {
Allocations.push_back({std::move(Memory)});
}

size_t size() const { return Allocations.size(); }
};

public:
DynamicAllocator() = default;
~DynamicAllocator();

void cleanup();

unsigned getNumAllocations() const { return AllocationSites.size(); }

/// Allocate ONE element of the given descriptor.
Block *allocate(const Descriptor *D, unsigned EvalID);
/// Allocate \p NumElements primitive elements of the given type.
Block *allocate(const Expr *Source, PrimType T, size_t NumElements,
unsigned EvalID);
/// Allocate \p NumElements elements of the given descriptor.
Block *allocate(const Descriptor *D, size_t NumElements, unsigned EvalID);

/// Deallocate the given source+block combination.
/// Returns \c true if anything has been deallocatd, \c false otherwise.
bool deallocate(const Expr *Source, const Block *BlockToDelete,
InterpState &S);

/// Checks whether the allocation done at the given source is an array
/// allocation.
bool isArrayAllocation(const Expr *Source) const {
if (auto It = AllocationSites.find(Source); It != AllocationSites.end())
return It->second.IsArrayAllocation;
return false;
}

/// Allocation site iterator.
using const_virtual_iter =
llvm::DenseMap<const Expr *, AllocationSite>::const_iterator;
llvm::iterator_range<const_virtual_iter> allocation_sites() const {
return llvm::make_range(AllocationSites.begin(), AllocationSites.end());
}

private:
llvm::DenseMap<const Expr *, AllocationSite> AllocationSites;

using PoolAllocTy = llvm::BumpPtrAllocatorImpl<llvm::MallocAllocator>;
PoolAllocTy DescAllocator;

/// Allocates a new descriptor.
template <typename... Ts> Descriptor *allocateDescriptor(Ts &&...Args) {
return new (DescAllocator) Descriptor(std::forward<Ts>(Args)...);
}
};

} // namespace interp
} // namespace clang
#endif
25 changes: 24 additions & 1 deletion clang/lib/AST/Interp/EvalEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,17 @@ bool EvalEmitter::fallthrough(const LabelTy &Label) {
return true;
}

static bool checkReturnState(InterpState &S) {
return S.maybeDiagnoseDanglingAllocations();
}

template <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) {
if (!isActive())
return true;

if (!checkReturnState(S))
return false;

using T = typename PrimConv<OpType>::T;
EvalResult.setValue(S.Stk.pop<T>().toAPValue());
return true;
Expand All @@ -147,12 +155,17 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) {

const Pointer &Ptr = S.Stk.pop<Pointer>();

if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))
return false;
if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
return false;

if (!checkReturnState(S))
return false;

// Implicitly convert lvalue to rvalue, if requested.
if (ConvertResultToRValue) {
if (!Ptr.isDereferencable())
if (!Ptr.isZero() && !Ptr.isDereferencable())
return false;
// Never allow reading from a non-const pointer, unless the memory
// has been created in this evaluation.
Expand All @@ -174,22 +187,32 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) {
template <> bool EvalEmitter::emitRet<PT_FnPtr>(const SourceInfo &Info) {
if (!isActive())
return true;

if (!checkReturnState(S))
return false;
// Function pointers cannot be converted to rvalues.
EvalResult.setFunctionPointer(S.Stk.pop<FunctionPointer>());
return true;
}

bool EvalEmitter::emitRetVoid(const SourceInfo &Info) {
if (!checkReturnState(S))
return false;
EvalResult.setValid();
return true;
}

bool EvalEmitter::emitRetValue(const SourceInfo &Info) {
const auto &Ptr = S.Stk.pop<Pointer>();

if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))
return false;
if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
return false;

if (!checkReturnState(S))
return false;

if (std::optional<APValue> APV =
Ptr.toRValue(S.getCtx(), EvalResult.getSourceType())) {
EvalResult.setValue(*APV);
Expand Down
72 changes: 72 additions & 0 deletions clang/lib/AST/Interp/EvaluationResult.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "InterpState.h"
#include "Record.h"
#include "clang/AST/ExprCXX.h"
#include "llvm/ADT/SetVector.h"

namespace clang {
namespace interp {
Expand Down Expand Up @@ -152,6 +153,11 @@ bool EvaluationResult::checkFullyInitialized(InterpState &S,
if (Ptr.isZero())
return true;

// We can't inspect dead pointers at all. Return true here so we can
// diagnose them later.
if (!Ptr.isLive())
return true;

SourceLocation InitLoc;
if (const auto *D = Source.dyn_cast<const Decl *>())
InitLoc = cast<VarDecl>(D)->getAnyInitializer()->getExprLoc();
Expand All @@ -168,5 +174,71 @@ bool EvaluationResult::checkFullyInitialized(InterpState &S,
return true;
}

static void collectBlocks(const Pointer &Ptr,
llvm::SetVector<const Block *> &Blocks) {
auto isUsefulPtr = [](const Pointer &P) -> bool {
return P.isLive() && !P.isZero() && !P.isDummy() &&
!P.isUnknownSizeArray() && !P.isOnePastEnd() && P.isBlockPointer();
};

if (!isUsefulPtr(Ptr))
return;

Blocks.insert(Ptr.block());

const Descriptor *Desc = Ptr.getFieldDesc();
if (!Desc)
return;

if (const Record *R = Desc->ElemRecord) {
for (const Record::Field &F : R->fields()) {
const Pointer &FieldPtr = Ptr.atField(F.Offset);
assert(FieldPtr.block() == Ptr.block());
collectBlocks(FieldPtr, Blocks);
}
} else if (Desc->isPrimitive() && Desc->getPrimType() == PT_Ptr) {
const Pointer &Pointee = Ptr.deref<Pointer>();
if (isUsefulPtr(Pointee) && !Blocks.contains(Pointee.block()))
collectBlocks(Pointee, Blocks);

} else if (Desc->isPrimitiveArray() && Desc->getPrimType() == PT_Ptr) {
for (unsigned I = 0; I != Desc->getNumElems(); ++I) {
const Pointer &ElemPointee = Ptr.atIndex(I).deref<Pointer>();
if (isUsefulPtr(ElemPointee) && !Blocks.contains(ElemPointee.block()))
collectBlocks(ElemPointee, Blocks);
}
} else if (Desc->isCompositeArray()) {
for (unsigned I = 0; I != Desc->getNumElems(); ++I) {
const Pointer &ElemPtr = Ptr.atIndex(I).narrow();
collectBlocks(ElemPtr, Blocks);
}
}
}

bool EvaluationResult::checkReturnValue(InterpState &S, const Context &Ctx,
const Pointer &Ptr,
const SourceInfo &Info) {
// Collect all blocks that this pointer (transitively) points to and
// return false if any of them is a dynamic block.
llvm::SetVector<const Block *> Blocks;

collectBlocks(Ptr, Blocks);

for (const Block *B : Blocks) {
if (B->isDynamic()) {
assert(B->getDescriptor());
assert(B->getDescriptor()->asExpr());

S.FFDiag(Info, diag::note_constexpr_dynamic_alloc)
<< Ptr.getType()->isReferenceType() << !Ptr.isRoot();
S.Note(B->getDescriptor()->asExpr()->getExprLoc(),
diag::note_constexpr_dynamic_alloc_here);
return false;
}
}

return true;
}

} // namespace interp
} // namespace clang
6 changes: 6 additions & 0 deletions clang/lib/AST/Interp/EvaluationResult.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,12 @@ class EvaluationResult final {
/// LValue and we can't read from it.
std::optional<APValue> toRValue() const;

/// Check that all subobjects of the given pointer have been initialized.
bool checkFullyInitialized(InterpState &S, const Pointer &Ptr) const;
/// Check that none of the blocks the given pointer (transitively) points
/// to are dynamically allocated.
bool checkReturnValue(InterpState &S, const Context &Ctx, const Pointer &Ptr,
const SourceInfo &Info);

QualType getSourceType() const {
if (const auto *D =
Expand All @@ -113,6 +118,7 @@ class EvaluationResult final {
void dump() const;

friend class EvalEmitter;
friend class InterpState;
};

} // namespace interp
Expand Down
52 changes: 52 additions & 0 deletions clang/lib/AST/Interp/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,58 @@ bool CheckFloatResult(InterpState &S, CodePtr OpPC, const Floating &Result,
return true;
}

bool CheckDynamicMemoryAllocation(InterpState &S, CodePtr OpPC) {
if (S.getLangOpts().CPlusPlus20)
return true;

const SourceInfo &E = S.Current->getSource(OpPC);
S.FFDiag(E, diag::note_constexpr_new);
return false;
}

bool CheckNewDeleteForms(InterpState &S, CodePtr OpPC, bool NewWasArray,
bool DeleteIsArray, const Descriptor *D,
const Expr *NewExpr) {
if (NewWasArray == DeleteIsArray)
return true;

QualType TypeToDiagnose;
// We need to shuffle things around a bit here to get a better diagnostic,
// because the expression we allocated the block for was of type int*,
// but we want to get the array size right.
if (D->isArray()) {
QualType ElemQT = D->getType()->getPointeeType();
TypeToDiagnose = S.getCtx().getConstantArrayType(
ElemQT, APInt(64, static_cast<uint64_t>(D->getNumElems()), false),
nullptr, ArraySizeModifier::Normal, 0);
} else
TypeToDiagnose = D->getType()->getPointeeType();

const SourceInfo &E = S.Current->getSource(OpPC);
S.FFDiag(E, diag::note_constexpr_new_delete_mismatch)
<< DeleteIsArray << 0 << TypeToDiagnose;
S.Note(NewExpr->getExprLoc(), diag::note_constexpr_dynamic_alloc_here)
<< NewExpr->getSourceRange();
return false;
}

bool CheckDeleteSource(InterpState &S, CodePtr OpPC, const Expr *Source,
const Pointer &Ptr) {
if (Source && isa<CXXNewExpr>(Source))
return true;

// Whatever this is, we didn't heap allocate it.
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.FFDiag(Loc, diag::note_constexpr_delete_not_heap_alloc)
<< Ptr.toDiagnosticString(S.getCtx());

if (Ptr.isTemporary())
S.Note(Ptr.getDeclLoc(), diag::note_constexpr_temporary_here);
else
S.Note(Ptr.getDeclLoc(), diag::note_declared_at);
return false;
}

/// We aleady know the given DeclRefExpr is invalid for some reason,
/// now figure out why and print appropriate diagnostics.
bool CheckDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR) {
Expand Down
155 changes: 153 additions & 2 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include "../ExprConstShared.h"
#include "Boolean.h"
#include "DynamicAllocator.h"
#include "Floating.h"
#include "Function.h"
#include "FunctionPointer.h"
Expand Down Expand Up @@ -122,6 +123,20 @@ bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD);
bool CheckNonNullArgs(InterpState &S, CodePtr OpPC, const Function *F,
const CallExpr *CE, unsigned ArgSize);

/// Checks if dynamic memory allocation is available in the current
/// language mode.
bool CheckDynamicMemoryAllocation(InterpState &S, CodePtr OpPC);

/// Diagnose mismatched new[]/delete or new/delete[] pairs.
bool CheckNewDeleteForms(InterpState &S, CodePtr OpPC, bool NewWasArray,
bool DeleteIsArray, const Descriptor *D,
const Expr *NewExpr);

/// Check the source of the pointer passed to delete/delete[] has actually
/// been heap allocated by us.
bool CheckDeleteSource(InterpState &S, CodePtr OpPC, const Expr *Source,
const Pointer &Ptr);

/// Sets the given integral value to the pointer, which is of
/// a std::{weak,partial,strong}_ordering type.
bool SetThreeWayComparisonField(InterpState &S, CodePtr OpPC,
Expand Down Expand Up @@ -189,6 +204,30 @@ bool CheckDivRem(InterpState &S, CodePtr OpPC, const T &LHS, const T &RHS) {
return true;
}

template <typename SizeT>
bool CheckArraySize(InterpState &S, CodePtr OpPC, SizeT *NumElements,
unsigned ElemSize, bool IsNoThrow) {
// FIXME: Both the SizeT::from() as well as the
// NumElements.toAPSInt() in this function are rather expensive.

// FIXME: GH63562
// APValue stores array extents as unsigned,
// so anything that is greater that unsigned would overflow when
// constructing the array, we catch this here.
SizeT MaxElements = SizeT::from(Descriptor::MaxArrayElemBytes / ElemSize);
if (NumElements->toAPSInt().getActiveBits() >
ConstantArrayType::getMaxSizeBits(S.getCtx()) ||
*NumElements > MaxElements) {
if (!IsNoThrow) {
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.FFDiag(Loc, diag::note_constexpr_new_too_large)
<< NumElements->toDiagnosticString(S.getCtx());
}
return false;
}
return true;
}

/// Checks if the result of a floating-point operation is valid
/// in the current context.
bool CheckFloatResult(InterpState &S, CodePtr OpPC, const Floating &Result,
Expand Down Expand Up @@ -1252,8 +1291,7 @@ bool GetGlobalUnchecked(InterpState &S, CodePtr OpPC, uint32_t I) {
const Pointer &Ptr = S.P.getPtrGlobal(I);
if (!Ptr.isInitialized())
return false;
const Block *B = S.P.getGlobal(I);
S.Stk.push<T>(B->deref<T>());
S.Stk.push<T>(Ptr.deref<T>());
return true;
}

Expand Down Expand Up @@ -2767,6 +2805,119 @@ inline bool CheckDecl(InterpState &S, CodePtr OpPC, const VarDecl *VD) {
return true;
}

inline bool Alloc(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
assert(Desc);

if (!CheckDynamicMemoryAllocation(S, OpPC))
return false;

DynamicAllocator &Allocator = S.getAllocator();
Block *B = Allocator.allocate(Desc, S.Ctx.getEvalID());
assert(B);

S.Stk.push<Pointer>(B, sizeof(InlineDescriptor));

return true;
}

template <PrimType Name, class SizeT = typename PrimConv<Name>::T>
inline bool AllocN(InterpState &S, CodePtr OpPC, PrimType T, const Expr *Source,
bool IsNoThrow) {
if (!CheckDynamicMemoryAllocation(S, OpPC))
return false;

SizeT NumElements = S.Stk.pop<SizeT>();
if (!CheckArraySize(S, OpPC, &NumElements, primSize(T), IsNoThrow)) {
if (!IsNoThrow)
return false;

// If this failed and is nothrow, just return a null ptr.
S.Stk.push<Pointer>(0, nullptr);
return true;
}

DynamicAllocator &Allocator = S.getAllocator();
Block *B = Allocator.allocate(Source, T, static_cast<size_t>(NumElements),
S.Ctx.getEvalID());
assert(B);
S.Stk.push<Pointer>(B, sizeof(InlineDescriptor));

return true;
}

template <PrimType Name, class SizeT = typename PrimConv<Name>::T>
inline bool AllocCN(InterpState &S, CodePtr OpPC, const Descriptor *ElementDesc,
bool IsNoThrow) {
if (!CheckDynamicMemoryAllocation(S, OpPC))
return false;

SizeT NumElements = S.Stk.pop<SizeT>();
if (!CheckArraySize(S, OpPC, &NumElements, ElementDesc->getSize(),
IsNoThrow)) {
if (!IsNoThrow)
return false;

// If this failed and is nothrow, just return a null ptr.
S.Stk.push<Pointer>(0, ElementDesc);
return true;
}

DynamicAllocator &Allocator = S.getAllocator();
Block *B = Allocator.allocate(ElementDesc, static_cast<size_t>(NumElements),
S.Ctx.getEvalID());
assert(B);

S.Stk.push<Pointer>(B, sizeof(InlineDescriptor));

return true;
}

static inline bool Free(InterpState &S, CodePtr OpPC, bool DeleteIsArrayForm) {

if (!CheckDynamicMemoryAllocation(S, OpPC))
return false;

const Expr *Source = nullptr;
const Block *BlockToDelete = nullptr;
{
// Extra scope for this so the block doesn't have this pointer
// pointing to it when we destroy it.
const Pointer &Ptr = S.Stk.pop<Pointer>();

// Deleteing nullptr is always fine.
if (Ptr.isZero())
return true;

if (!Ptr.isRoot() || Ptr.isOnePastEnd() || Ptr.isArrayElement()) {
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.FFDiag(Loc, diag::note_constexpr_delete_subobject)
<< Ptr.toDiagnosticString(S.getCtx()) << Ptr.isOnePastEnd();
return false;
}

Source = Ptr.getDeclDesc()->asExpr();
BlockToDelete = Ptr.block();

if (!CheckDeleteSource(S, OpPC, Source, Ptr))
return false;
}
assert(Source);
assert(BlockToDelete);

DynamicAllocator &Allocator = S.getAllocator();
bool WasArrayAlloc = Allocator.isArrayAllocation(Source);
const Descriptor *BlockDesc = BlockToDelete->getDescriptor();

if (!Allocator.deallocate(Source, BlockToDelete, S)) {
// Nothing has been deallocated, this must be a double-delete.
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.FFDiag(Loc, diag::note_constexpr_double_delete);
return false;
}
return CheckNewDeleteForms(S, OpPC, WasArrayAlloc, DeleteIsArrayForm,
BlockDesc, Source);
}

//===----------------------------------------------------------------------===//
// Read opcode arguments
//===----------------------------------------------------------------------===//
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/Interp/InterpBlock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ DeadBlock::DeadBlock(DeadBlock *&Root, Block *Blk)
}

void DeadBlock::free() {
if (B.IsInitialized)
B.invokeDtor();

if (Prev)
Prev->Next = Next;
if (Next)
Expand Down
20 changes: 10 additions & 10 deletions clang/lib/AST/Interp/InterpBlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ class Block final {
Block(unsigned EvalID, const std::optional<unsigned> &DeclID,
const Descriptor *Desc, bool IsStatic = false, bool IsExtern = false)
: EvalID(EvalID), DeclID(DeclID), IsStatic(IsStatic), IsExtern(IsExtern),
Desc(Desc) {
IsDynamic(false), Desc(Desc) {
assert(Desc);
}

Block(unsigned EvalID, const Descriptor *Desc, bool IsStatic = false,
bool IsExtern = false)
: EvalID(EvalID), DeclID((unsigned)-1), IsStatic(IsStatic),
IsExtern(IsExtern), Desc(Desc) {
IsExtern(IsExtern), IsDynamic(false), Desc(Desc) {
assert(Desc);
}

Expand All @@ -73,6 +73,7 @@ class Block final {
bool isStatic() const { return IsStatic; }
/// Checks if the block is temporary.
bool isTemporary() const { return Desc->IsTemporary; }
bool isDynamic() const { return IsDynamic; }
/// Returns the size of the block.
unsigned getSize() const { return Desc->getAllocSize(); }
/// Returns the declaration ID.
Expand Down Expand Up @@ -105,15 +106,9 @@ class Block final {
return reinterpret_cast<const std::byte *>(this) + sizeof(Block);
}

/// Returns a view over the data.
template <typename T>
T &deref() { return *reinterpret_cast<T *>(data()); }
template <typename T> const T &deref() const {
return *reinterpret_cast<const T *>(data());
}

/// Invokes the constructor.
void invokeCtor() {
assert(!IsInitialized);
std::memset(rawData(), 0, Desc->getAllocSize());
if (Desc->CtorFn)
Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable,
Expand All @@ -123,6 +118,7 @@ class Block final {

/// Invokes the Destructor.
void invokeDtor() {
assert(IsInitialized);
if (Desc->DtorFn)
Desc->DtorFn(this, data(), Desc);
IsInitialized = false;
Expand All @@ -135,11 +131,12 @@ class Block final {
friend class Pointer;
friend class DeadBlock;
friend class InterpState;
friend class DynamicAllocator;

Block(unsigned EvalID, const Descriptor *Desc, bool IsExtern, bool IsStatic,
bool IsDead)
: EvalID(EvalID), IsStatic(IsStatic), IsExtern(IsExtern), IsDead(true),
Desc(Desc) {
IsDynamic(false), Desc(Desc) {
assert(Desc);
}

Expand Down Expand Up @@ -169,6 +166,9 @@ class Block final {
/// Flag indicating if the block contents have been initialized
/// via invokeCtor.
bool IsInitialized = false;
/// Flag indicating if this block has been allocated via dynamic
/// memory allocation (e.g. malloc).
bool IsDynamic = false;
/// Pointer to the stack slot descriptor.
const Descriptor *Desc;
};
Expand Down
5 changes: 4 additions & 1 deletion clang/lib/AST/Interp/InterpFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,9 @@ void InterpFrame::describe(llvm::raw_ostream &OS) const {
// diagnose them. The 'in call to' diagnostics for them add no value to the
// user _and_ it doesn't generally work since the argument types don't always
// match the function prototype. Just ignore them.
if (const auto *F = getFunction(); F && F->isBuiltin())
// Similarly, for lambda static invokers, we would just print __invoke().
if (const auto *F = getFunction();
F && (F->isBuiltin() || F->isLambdaStaticInvoker()))
return;

const FunctionDecl *F = getCallee();
Expand Down Expand Up @@ -225,6 +227,7 @@ Pointer InterpFrame::getParamPointer(unsigned Off) {
size_t BlockSize = sizeof(Block) + Desc.second->getAllocSize();
auto Memory = std::make_unique<char[]>(BlockSize);
auto *B = new (Memory.get()) Block(S.Ctx.getEvalID(), Desc.second);
B->invokeCtor();

// Copy the initial value.
TYPE_SWITCH(Desc.first, new (B->data()) T(stackRef<T>(Off)));
Expand Down
23 changes: 21 additions & 2 deletions clang/lib/AST/Interp/InterpState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ void InterpState::cleanup() {
P->PointeeStorage.BS.Pointee = nullptr;
}
}

Alloc.cleanup();
}

Frame *InterpState::getCurrentFrame() {
Expand All @@ -67,17 +69,34 @@ void InterpState::deallocate(Block *B) {
char *Memory =
reinterpret_cast<char *>(std::malloc(sizeof(DeadBlock) + Size));
auto *D = new (Memory) DeadBlock(DeadBlocks, B);
std::memset(D->B.rawData(), 0, D->B.getSize());

// Move data and metadata from the old block to the new (dead)block.
if (Desc->MoveFn) {
if (B->IsInitialized && Desc->MoveFn) {
Desc->MoveFn(B, B->data(), D->data(), Desc);
if (Desc->getMetadataSize() > 0)
std::memcpy(D->rawData(), B->rawData(), Desc->getMetadataSize());
}
D->B.IsInitialized = B->IsInitialized;

// We moved the contents over to the DeadBlock.
B->IsInitialized = false;
} else {
} else if (B->IsInitialized) {
B->invokeDtor();
}
}

bool InterpState::maybeDiagnoseDanglingAllocations() {
bool NoAllocationsLeft = (Alloc.getNumAllocations() == 0);

if (!checkingPotentialConstantExpression()) {
for (const auto &It : Alloc.allocation_sites()) {
assert(It.second.size() > 0);

const Expr *Source = It.first;
CCEDiag(Source->getExprLoc(), diag::note_constexpr_memory_leak)
<< (It.second.size() - 1) << Source->getSourceRange();
}
}
return NoAllocationsLeft;
}
11 changes: 11 additions & 0 deletions clang/lib/AST/Interp/InterpState.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#define LLVM_CLANG_AST_INTERP_INTERPSTATE_H

#include "Context.h"
#include "DynamicAllocator.h"
#include "Function.h"
#include "InterpFrame.h"
#include "InterpStack.h"
Expand Down Expand Up @@ -102,13 +103,23 @@ class InterpState final : public State, public SourceMapper {

void setEvalLocation(SourceLocation SL) { this->EvalLocation = SL; }

DynamicAllocator &getAllocator() { return Alloc; }

/// Diagnose any dynamic allocations that haven't been freed yet.
/// Will return \c false if there were any allocations to diagnose,
/// \c true otherwise.
bool maybeDiagnoseDanglingAllocations();

private:
friend class EvaluationResult;
/// AST Walker state.
State &Parent;
/// Dead block chain.
DeadBlock *DeadBlocks = nullptr;
/// Reference to the offset-source mapping.
SourceMapper *M;
/// Allocator used for dynamic allocations performed via the program.
DynamicAllocator Alloc;

public:
/// Reference to the module containing all bytecode.
Expand Down
24 changes: 23 additions & 1 deletion clang/lib/AST/Interp/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,14 @@ def ArgRoundingMode : ArgType { let Name = "llvm::RoundingMode"; }
def ArgLETD: ArgType { let Name = "const LifetimeExtendedTemporaryDecl *"; }
def ArgCastKind : ArgType { let Name = "CastKind"; }
def ArgCallExpr : ArgType { let Name = "const CallExpr *"; }
def ArgExpr : ArgType { let Name = "const Expr *"; }
def ArgOffsetOfExpr : ArgType { let Name = "const OffsetOfExpr *"; }
def ArgDeclRef : ArgType { let Name = "const DeclRefExpr *"; }
def ArgDesc : ArgType { let Name = "const Descriptor *"; }
def ArgCCI : ArgType { let Name = "const ComparisonCategoryInfo *"; }
def ArgDecl : ArgType { let Name = "const Decl*"; }
def ArgVarDecl : ArgType { let Name = "const VarDecl*"; }
def ArgDesc : ArgType { let Name = "const Descriptor *"; }
def ArgPrimType : ArgType { let Name = "PrimType"; }

//===----------------------------------------------------------------------===//
// Classes of types instructions operate on.
Expand Down Expand Up @@ -747,3 +749,23 @@ def GetMemberPtrDecl : Opcode;
// Debugging.
//===----------------------------------------------------------------------===//
def Dump : Opcode;

def Alloc : Opcode {
let Args = [ArgDesc];
}

def AllocN : Opcode {
let Types = [IntegerTypeClass];
let Args = [ArgPrimType, ArgExpr, ArgBool];
let HasGroup = 1;
}

def AllocCN : Opcode {
let Types = [IntegerTypeClass];
let Args = [ArgDesc, ArgBool];
let HasGroup = 1;
}

def Free : Opcode {
let Args = [ArgBool];
}
5 changes: 3 additions & 2 deletions clang/lib/AST/Interp/Pointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,9 @@ APValue Pointer::toAPValue() const {
Pointer Ptr = *this;
while (Ptr.isField() || Ptr.isArrayElement()) {
if (Ptr.isArrayRoot()) {
Path.push_back(APValue::LValuePathEntry::ArrayIndex(0));
Ptr = Ptr.getBase();
Path.push_back(APValue::LValuePathEntry(
{Ptr.getFieldDesc()->asDecl(), /*IsVirtual=*/false}));
Ptr = Ptr.getBase();
} else if (Ptr.isArrayElement()) {
if (Ptr.isOnePastEnd())
Path.push_back(APValue::LValuePathEntry::ArrayIndex(Ptr.getArray().getNumElems()));
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/Pointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,7 @@ class Pointer {
friend class MemberPointer;
friend class InterpState;
friend struct InitMap;
friend class DynamicAllocator;

Pointer(Block *Pointee, unsigned Base, uint64_t Offset);

Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/MicrosoftMangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,7 @@ void MicrosoftCXXNameMangler::mangleFloat(llvm::APFloat Number) {
case APFloat::S_IEEEquad: Out << 'Y'; break;
case APFloat::S_PPCDoubleDouble: Out << 'Z'; break;
case APFloat::S_Float8E5M2:
case APFloat::S_Float8E4M3:
case APFloat::S_Float8E4M3FN:
case APFloat::S_Float8E5M2FNUZ:
case APFloat::S_Float8E4M3FNUZ:
Expand Down
18 changes: 18 additions & 0 deletions clang/lib/AST/StmtOpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,24 @@ OMPUnrollDirective *OMPUnrollDirective::CreateEmpty(const ASTContext &C,
SourceLocation(), SourceLocation());
}

OMPReverseDirective *
OMPReverseDirective::Create(const ASTContext &C, SourceLocation StartLoc,
SourceLocation EndLoc, Stmt *AssociatedStmt,
Stmt *TransformedStmt, Stmt *PreInits) {
OMPReverseDirective *Dir = createDirective<OMPReverseDirective>(
C, std::nullopt, AssociatedStmt, TransformedStmtOffset + 1, StartLoc,
EndLoc);
Dir->setTransformedStmt(TransformedStmt);
Dir->setPreInits(PreInits);
return Dir;
}

OMPReverseDirective *OMPReverseDirective::CreateEmpty(const ASTContext &C) {
return createEmptyDirective<OMPReverseDirective>(
C, /*NumClauses=*/0, /*HasAssociatedStmt=*/true,
TransformedStmtOffset + 1, SourceLocation(), SourceLocation());
}

OMPForSimdDirective *
OMPForSimdDirective::Create(const ASTContext &C, SourceLocation StartLoc,
SourceLocation EndLoc, unsigned CollapsedNum,
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/AST/StmtPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,11 @@ void StmtPrinter::VisitOMPUnrollDirective(OMPUnrollDirective *Node) {
PrintOMPExecutableDirective(Node);
}

void StmtPrinter::VisitOMPReverseDirective(OMPReverseDirective *Node) {
Indent() << "#pragma omp reverse";
PrintOMPExecutableDirective(Node);
}

void StmtPrinter::VisitOMPForDirective(OMPForDirective *Node) {
Indent() << "#pragma omp for";
PrintOMPExecutableDirective(Node);
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/StmtProfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -985,6 +985,10 @@ void StmtProfiler::VisitOMPUnrollDirective(const OMPUnrollDirective *S) {
VisitOMPLoopTransformationDirective(S);
}

void StmtProfiler::VisitOMPReverseDirective(const OMPReverseDirective *S) {
VisitOMPLoopTransformationDirective(S);
}

void StmtProfiler::VisitOMPForDirective(const OMPForDirective *S) {
VisitOMPLoopDirective(S);
}
Expand Down
Loading