Vendor import of clang trunk r291476:
https://llvm.org/svn/llvm-project/cfe/trunk@291476
This commit is contained in:
parent
ffb9ad6832
commit
0778366e3c
@ -47,6 +47,10 @@ sections with improvements to Clang's support for those languages.
|
||||
Major New Features
|
||||
------------------
|
||||
|
||||
- The ``diagnose_if`` attribute has been added to clang. This attribute allows
|
||||
clang to emit a warning or error if a function call meets one or more
|
||||
user-specified conditions.
|
||||
|
||||
- ...
|
||||
|
||||
Improvements to Clang's diagnostics
|
||||
|
@ -651,7 +651,8 @@ public:
|
||||
/// constant.
|
||||
bool EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx,
|
||||
const FunctionDecl *Callee,
|
||||
ArrayRef<const Expr*> Args) const;
|
||||
ArrayRef<const Expr*> Args,
|
||||
const Expr *This = nullptr) const;
|
||||
|
||||
/// \brief If the current Expr is a pointer, this will try to statically
|
||||
/// determine the number of bytes available where the pointer is pointing.
|
||||
|
@ -140,12 +140,15 @@ class Argument<string name, bit optional, bit fake = 0> {
|
||||
bit Fake = fake;
|
||||
}
|
||||
|
||||
class BoolArgument<string name, bit opt = 0> : Argument<name, opt>;
|
||||
class BoolArgument<string name, bit opt = 0, bit fake = 0> : Argument<name, opt,
|
||||
fake>;
|
||||
class IdentifierArgument<string name, bit opt = 0> : Argument<name, opt>;
|
||||
class IntArgument<string name, bit opt = 0> : Argument<name, opt>;
|
||||
class StringArgument<string name, bit opt = 0> : Argument<name, opt>;
|
||||
class ExprArgument<string name, bit opt = 0> : Argument<name, opt>;
|
||||
class FunctionArgument<string name, bit opt = 0> : Argument<name, opt>;
|
||||
class FunctionArgument<string name, bit opt = 0, bit fake = 0> : Argument<name,
|
||||
opt,
|
||||
fake>;
|
||||
class TypeArgument<string name, bit opt = 0> : Argument<name, opt>;
|
||||
class UnsignedArgument<string name, bit opt = 0> : Argument<name, opt>;
|
||||
class VariadicUnsignedArgument<string name> : Argument<name, 1>;
|
||||
@ -1591,6 +1594,26 @@ def Unavailable : InheritableAttr {
|
||||
let Documentation = [Undocumented];
|
||||
}
|
||||
|
||||
def DiagnoseIf : InheritableAttr {
|
||||
let Spellings = [GNU<"diagnose_if">];
|
||||
let Subjects = SubjectList<[Function]>;
|
||||
let Args = [ExprArgument<"Cond">, StringArgument<"Message">,
|
||||
EnumArgument<"DiagnosticType",
|
||||
"DiagnosticType",
|
||||
["error", "warning"],
|
||||
["DT_Error", "DT_Warning"]>,
|
||||
BoolArgument<"ArgDependent", 0, /*fake*/ 1>,
|
||||
FunctionArgument<"Parent", 0, /*fake*/ 1>];
|
||||
let DuplicatesAllowedWhileMerging = 1;
|
||||
let LateParsed = 1;
|
||||
let AdditionalMembers = [{
|
||||
bool isError() const { return diagnosticType == DT_Error; }
|
||||
bool isWarning() const { return diagnosticType == DT_Warning; }
|
||||
}];
|
||||
let TemplateDependent = 1;
|
||||
let Documentation = [DiagnoseIfDocs];
|
||||
}
|
||||
|
||||
def ArcWeakrefUnavailable : InheritableAttr {
|
||||
let Spellings = [GNU<"objc_arc_weak_reference_unavailable">];
|
||||
let Subjects = SubjectList<[ObjCInterface], ErrorDiag>;
|
||||
|
@ -378,6 +378,65 @@ template instantiation, so the value for ``T::number`` is known.
|
||||
}];
|
||||
}
|
||||
|
||||
def DiagnoseIfDocs : Documentation {
|
||||
let Category = DocCatFunction;
|
||||
let Content = [{
|
||||
The ``diagnose_if`` attribute can be placed on function declarations to emit
|
||||
warnings or errors at compile-time if calls to the attributed function meet
|
||||
certain user-defined criteria. For example:
|
||||
|
||||
.. code-block:: c
|
||||
void abs(int a)
|
||||
__attribute__((diagnose_if(a >= 0, "Redundant abs call", "warning")));
|
||||
void must_abs(int a)
|
||||
__attribute__((diagnose_if(a >= 0, "Redundant abs call", "error")));
|
||||
|
||||
int val = abs(1); // warning: Redundant abs call
|
||||
int val2 = must_abs(1); // error: Redundant abs call
|
||||
int val3 = abs(val);
|
||||
int val4 = must_abs(val); // Because run-time checks are not emitted for
|
||||
// diagnose_if attributes, this executes without
|
||||
// issue.
|
||||
|
||||
|
||||
``diagnose_if`` is closely related to ``enable_if``, with a few key differences:
|
||||
|
||||
* Overload resolution is not aware of ``diagnose_if`` attributes: they're
|
||||
considered only after we select the best candidate from a given candidate set.
|
||||
* Function declarations that differ only in their ``diagnose_if`` attributes are
|
||||
considered to be redeclarations of the same function (not overloads).
|
||||
* If the condition provided to ``diagnose_if`` cannot be evaluated, no
|
||||
diagnostic will be emitted.
|
||||
|
||||
Otherwise, ``diagnose_if`` is essentially the logical negation of ``enable_if``.
|
||||
|
||||
As a result of bullet number two, ``diagnose_if`` attributes will stack on the
|
||||
same function. For example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int foo() __attribute__((diagnose_if(1, "diag1", "warning")));
|
||||
int foo() __attribute__((diagnose_if(1, "diag2", "warning")));
|
||||
|
||||
int bar = foo(); // warning: diag1
|
||||
// warning: diag2
|
||||
int (*fooptr)(void) = foo; // warning: diag1
|
||||
// warning: diag2
|
||||
|
||||
constexpr int supportsAPILevel(int N) { return N < 5; }
|
||||
int baz(int a)
|
||||
__attribute__((diagnose_if(!supportsAPILevel(10),
|
||||
"Upgrade to API level 10 to use baz", "error")));
|
||||
int baz(int a)
|
||||
__attribute__((diagnose_if(!a, "0 is not recommended.", "warning")));
|
||||
|
||||
int (*bazptr)(int) = baz; // error: Upgrade to API level 10 to use baz
|
||||
int v = baz(0); // error: Upgrade to API level 10 to use baz
|
||||
|
||||
Query for this feature with ``__has_attribute(diagnose_if)``.
|
||||
}];
|
||||
}
|
||||
|
||||
def PassObjectSizeDocs : Documentation {
|
||||
let Category = DocCatVariable; // Technically it's a parameter doc, but eh.
|
||||
let Content = [{
|
||||
|
@ -161,6 +161,8 @@ def ext_old_implicitly_unsigned_long_cxx : ExtWarn<
|
||||
InGroup<CXX11Compat>;
|
||||
def ext_clang_enable_if : Extension<"'enable_if' is a clang extension">,
|
||||
InGroup<GccCompat>;
|
||||
def ext_clang_diagnose_if : Extension<"'diagnose_if' is a clang extension">,
|
||||
InGroup<GccCompat>;
|
||||
|
||||
// SEH
|
||||
def err_seh_expected_handler : Error<
|
||||
|
@ -495,6 +495,7 @@ def UnusedPropertyIvar : DiagGroup<"unused-property-ivar">;
|
||||
def UnusedGetterReturnValue : DiagGroup<"unused-getter-return-value">;
|
||||
def UsedButMarkedUnused : DiagGroup<"used-but-marked-unused">;
|
||||
def UserDefinedLiterals : DiagGroup<"user-defined-literals">;
|
||||
def UserDefinedWarnings : DiagGroup<"user-defined-warnings">;
|
||||
def Reorder : DiagGroup<"reorder">;
|
||||
def UndeclaredSelector : DiagGroup<"undeclared-selector">;
|
||||
def ImplicitAtomic : DiagGroup<"implicit-atomic-properties">;
|
||||
@ -683,7 +684,8 @@ def Most : DiagGroup<"most", [
|
||||
OverloadedVirtual,
|
||||
PrivateExtern,
|
||||
SelTypeCast,
|
||||
ExternCCompat
|
||||
ExternCCompat,
|
||||
UserDefinedWarnings
|
||||
]>;
|
||||
|
||||
// Thread Safety warnings
|
||||
|
@ -2141,8 +2141,11 @@ def err_constexpr_local_var_no_init : Error<
|
||||
def ext_constexpr_function_never_constant_expr : ExtWarn<
|
||||
"constexpr %select{function|constructor}0 never produces a "
|
||||
"constant expression">, InGroup<DiagGroup<"invalid-constexpr">>, DefaultError;
|
||||
def err_enable_if_never_constant_expr : Error<
|
||||
"'enable_if' attribute expression never produces a constant expression">;
|
||||
def err_attr_cond_never_constant_expr : Error<
|
||||
"%0 attribute expression never produces a constant expression">;
|
||||
def err_diagnose_if_invalid_diagnostic_type : Error<
|
||||
"invalid diagnostic type for 'diagnose_if'; use \"error\" or \"warning\" "
|
||||
"instead">;
|
||||
def err_constexpr_body_no_return : Error<
|
||||
"no return statement in constexpr function">;
|
||||
def err_constexpr_return_missing_expr : Error<
|
||||
@ -3333,6 +3336,9 @@ def note_ovl_candidate : Note<"candidate "
|
||||
|
||||
def note_ovl_candidate_inherited_constructor : Note<
|
||||
"constructor from base class %0 inherited here">;
|
||||
def note_ovl_candidate_inherited_constructor_slice : Note<
|
||||
"constructor inherited from base class cannot be used to initialize from "
|
||||
"an argument of the derived class type">;
|
||||
def note_ovl_candidate_illegal_constructor : Note<
|
||||
"candidate %select{constructor|template}0 ignored: "
|
||||
"instantiation %select{takes|would take}0 its own class type by value">;
|
||||
@ -3366,7 +3372,9 @@ def note_ovl_candidate_disabled_by_enable_if : Note<
|
||||
def note_ovl_candidate_has_pass_object_size_params: Note<
|
||||
"candidate address cannot be taken because parameter %0 has "
|
||||
"pass_object_size attribute">;
|
||||
def note_ovl_candidate_disabled_by_enable_if_attr : Note<
|
||||
def err_diagnose_if_succeeded : Error<"%0">;
|
||||
def warn_diagnose_if_succeeded : Warning<"%0">, InGroup<UserDefinedWarnings>;
|
||||
def note_ovl_candidate_disabled_by_function_cond_attr : Note<
|
||||
"candidate disabled: %0">;
|
||||
def note_ovl_candidate_disabled_by_extension : Note<
|
||||
"candidate disabled due to OpenCL extension">;
|
||||
@ -4395,6 +4403,7 @@ def note_not_found_by_two_phase_lookup : Note<"%0 should be declared prior to th
|
||||
def err_undeclared_use : Error<"use of undeclared %0">;
|
||||
def warn_deprecated : Warning<"%0 is deprecated">,
|
||||
InGroup<DeprecatedDeclarations>;
|
||||
def note_from_diagnose_if : Note<"from 'diagnose_if' attribute on %0:">;
|
||||
def warn_property_method_deprecated :
|
||||
Warning<"property access is using %0 method which is deprecated">,
|
||||
InGroup<DeprecatedDeclarations>;
|
||||
|
@ -146,6 +146,7 @@ LANGOPT(Modules , 1, 0, "modules extension to C")
|
||||
COMPATIBLE_LANGOPT(ModulesTS , 1, 0, "C++ Modules TS")
|
||||
BENIGN_ENUM_LANGOPT(CompilingModule, CompilingModuleKind, 2, CMK_None,
|
||||
"compiling a module interface")
|
||||
BENIGN_LANGOPT(CompilingPCH, 1, 0, "building a pch")
|
||||
COMPATIBLE_LANGOPT(ModulesDeclUse , 1, 0, "require declaration of module uses")
|
||||
BENIGN_LANGOPT(ModulesSearchAll , 1, 1, "searching even non-imported modules to find unresolved references")
|
||||
COMPATIBLE_LANGOPT(ModulesStrictDeclUse, 1, 0, "requiring declaration of module uses and all headers to be in modules")
|
||||
|
@ -167,6 +167,9 @@ def disable_llvm_passes : Flag<["-"], "disable-llvm-passes">,
|
||||
"frontend by not running any LLVM passes at all">;
|
||||
def disable_llvm_optzns : Flag<["-"], "disable-llvm-optzns">,
|
||||
Alias<disable_llvm_passes>;
|
||||
def disable_lifetimemarkers : Flag<["-"], "disable-lifetime-markers">,
|
||||
HelpText<"Disable lifetime-markers emission even when optimizations are "
|
||||
"enabled">;
|
||||
def disable_red_zone : Flag<["-"], "disable-red-zone">,
|
||||
HelpText<"Do not emit code that uses the red zone.">;
|
||||
def dwarf_column_info : Flag<["-"], "dwarf-column-info">,
|
||||
|
@ -52,6 +52,7 @@ CODEGENOPT(DisableGCov , 1, 0) ///< Don't run the GCov pass, for testing.
|
||||
CODEGENOPT(DisableLLVMPasses , 1, 0) ///< Don't run any LLVM IR passes to get
|
||||
///< the pristine IR generated by the
|
||||
///< frontend.
|
||||
CODEGENOPT(DisableLifetimeMarkers, 1, 0) ///< Don't emit any lifetime markers
|
||||
CODEGENOPT(ExperimentalNewPassManager, 1, 0) ///< Enables the new, experimental
|
||||
///< pass manager.
|
||||
CODEGENOPT(DisableRedZone , 1, 0) ///< Set when -mno-red-zone is enabled.
|
||||
|
@ -88,6 +88,8 @@ public:
|
||||
static std::unique_ptr<raw_pwrite_stream>
|
||||
ComputeASTConsumerArguments(CompilerInstance &CI, StringRef InFile,
|
||||
std::string &Sysroot, std::string &OutputFile);
|
||||
|
||||
bool BeginSourceFileAction(CompilerInstance &CI, StringRef Filename) override;
|
||||
};
|
||||
|
||||
class GenerateModuleAction : public ASTFrontendAction {
|
||||
|
@ -59,6 +59,13 @@ enum class SymbolLanguage {
|
||||
CXX,
|
||||
};
|
||||
|
||||
/// Language specific sub-kinds.
|
||||
enum class SymbolSubKind {
|
||||
None,
|
||||
CXXCopyConstructor,
|
||||
CXXMoveConstructor,
|
||||
};
|
||||
|
||||
/// Set of properties that provide additional info about a symbol.
|
||||
enum class SymbolProperty : uint8_t {
|
||||
Generic = 1 << 0,
|
||||
@ -107,6 +114,7 @@ struct SymbolRelation {
|
||||
|
||||
struct SymbolInfo {
|
||||
SymbolKind Kind;
|
||||
SymbolSubKind SubKind;
|
||||
SymbolPropertySet Properties;
|
||||
SymbolLanguage Lang;
|
||||
};
|
||||
@ -121,6 +129,7 @@ void printSymbolRoles(SymbolRoleSet Roles, raw_ostream &OS);
|
||||
bool printSymbolName(const Decl *D, const LangOptions &LO, raw_ostream &OS);
|
||||
|
||||
StringRef getSymbolKindString(SymbolKind K);
|
||||
StringRef getSymbolSubKindString(SymbolSubKind K);
|
||||
StringRef getSymbolLanguageString(SymbolLanguage K);
|
||||
|
||||
void applyForEachSymbolProperty(SymbolPropertySet Props,
|
||||
|
@ -215,14 +215,14 @@ public:
|
||||
|
||||
/// \brief Create the initialization entity for a parameter.
|
||||
static InitializedEntity InitializeParameter(ASTContext &Context,
|
||||
ParmVarDecl *Parm) {
|
||||
const ParmVarDecl *Parm) {
|
||||
return InitializeParameter(Context, Parm, Parm->getType());
|
||||
}
|
||||
|
||||
/// \brief Create the initialization entity for a parameter, but use
|
||||
/// another type.
|
||||
static InitializedEntity InitializeParameter(ASTContext &Context,
|
||||
ParmVarDecl *Parm,
|
||||
const ParmVarDecl *Parm,
|
||||
QualType Type) {
|
||||
bool Consumed = (Context.getLangOpts().ObjCAutoRefCount &&
|
||||
Parm->hasAttr<NSConsumedAttr>());
|
||||
|
@ -531,6 +531,13 @@ namespace clang {
|
||||
Ambiguous.construct();
|
||||
}
|
||||
|
||||
void setAsIdentityConversion(QualType T) {
|
||||
setStandard();
|
||||
Standard.setAsIdentityConversion();
|
||||
Standard.setFromType(T);
|
||||
Standard.setAllToTypes(T);
|
||||
}
|
||||
|
||||
/// \brief Whether the target is really a std::initializer_list, and the
|
||||
/// sequence only represents the worst element conversion.
|
||||
bool isStdInitializerListElement() const {
|
||||
@ -601,8 +608,17 @@ namespace clang {
|
||||
|
||||
/// This candidate was not viable because its OpenCL extension is disabled.
|
||||
ovl_fail_ext_disabled,
|
||||
|
||||
/// This inherited constructor is not viable because it would slice the
|
||||
/// argument.
|
||||
ovl_fail_inhctor_slice,
|
||||
};
|
||||
|
||||
/// A list of implicit conversion sequences for the arguments of an
|
||||
/// OverloadCandidate.
|
||||
typedef llvm::MutableArrayRef<ImplicitConversionSequence>
|
||||
ConversionSequenceList;
|
||||
|
||||
/// OverloadCandidate - A single candidate in an overload set (C++ 13.3).
|
||||
struct OverloadCandidate {
|
||||
/// Function - The actual function that this candidate
|
||||
@ -627,18 +643,13 @@ namespace clang {
|
||||
/// is a surrogate, but only if IsSurrogate is true.
|
||||
CXXConversionDecl *Surrogate;
|
||||
|
||||
/// Conversions - The conversion sequences used to convert the
|
||||
/// function arguments to the function parameters, the pointer points to a
|
||||
/// fixed size array with NumConversions elements. The memory is owned by
|
||||
/// the OverloadCandidateSet.
|
||||
ImplicitConversionSequence *Conversions;
|
||||
/// The conversion sequences used to convert the function arguments
|
||||
/// to the function parameters.
|
||||
ConversionSequenceList Conversions;
|
||||
|
||||
/// The FixIt hints which can be used to fix the Bad candidate.
|
||||
ConversionFixItGenerator Fix;
|
||||
|
||||
/// NumConversions - The number of elements in the Conversions array.
|
||||
unsigned NumConversions;
|
||||
|
||||
/// Viable - True to indicate that this overload candidate is viable.
|
||||
bool Viable;
|
||||
|
||||
@ -664,6 +675,26 @@ namespace clang {
|
||||
/// to be used while performing partial ordering of function templates.
|
||||
unsigned ExplicitCallArguments;
|
||||
|
||||
/// The number of diagnose_if attributes that this overload triggered.
|
||||
/// If any of the triggered attributes are errors, this won't count
|
||||
/// diagnose_if warnings.
|
||||
unsigned NumTriggeredDiagnoseIfs = 0;
|
||||
|
||||
/// Basically a TinyPtrVector<DiagnoseIfAttr *> that doesn't own the vector:
|
||||
/// If NumTriggeredDiagnoseIfs is 0 or 1, this is a DiagnoseIfAttr *,
|
||||
/// otherwise it's a pointer to an array of `NumTriggeredDiagnoseIfs`
|
||||
/// DiagnoseIfAttr *s.
|
||||
llvm::PointerUnion<DiagnoseIfAttr *, DiagnoseIfAttr **> DiagnoseIfInfo;
|
||||
|
||||
/// Gets an ArrayRef for the data at DiagnoseIfInfo. Note that this may give
|
||||
/// you a pointer into DiagnoseIfInfo.
|
||||
ArrayRef<DiagnoseIfAttr *> getDiagnoseIfInfo() const {
|
||||
auto *Ptr = NumTriggeredDiagnoseIfs <= 1
|
||||
? DiagnoseIfInfo.getAddrOfPtr1()
|
||||
: DiagnoseIfInfo.get<DiagnoseIfAttr **>();
|
||||
return {Ptr, NumTriggeredDiagnoseIfs};
|
||||
}
|
||||
|
||||
union {
|
||||
DeductionFailureInfo DeductionFailure;
|
||||
|
||||
@ -677,9 +708,9 @@ namespace clang {
|
||||
/// hasAmbiguousConversion - Returns whether this overload
|
||||
/// candidate requires an ambiguous conversion or not.
|
||||
bool hasAmbiguousConversion() const {
|
||||
for (unsigned i = 0, e = NumConversions; i != e; ++i) {
|
||||
if (!Conversions[i].isInitialized()) return false;
|
||||
if (Conversions[i].isAmbiguous()) return true;
|
||||
for (auto &C : Conversions) {
|
||||
if (!C.isInitialized()) return false;
|
||||
if (C.isAmbiguous()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -728,17 +759,42 @@ namespace clang {
|
||||
SmallVector<OverloadCandidate, 16> Candidates;
|
||||
llvm::SmallPtrSet<Decl *, 16> Functions;
|
||||
|
||||
// Allocator for OverloadCandidate::Conversions. We store the first few
|
||||
// elements inline to avoid allocation for small sets.
|
||||
llvm::BumpPtrAllocator ConversionSequenceAllocator;
|
||||
// Allocator for ConversionSequenceLists and DiagnoseIfAttr* arrays.
|
||||
// We store the first few of each of these inline to avoid allocation for
|
||||
// small sets.
|
||||
llvm::BumpPtrAllocator SlabAllocator;
|
||||
|
||||
SourceLocation Loc;
|
||||
CandidateSetKind Kind;
|
||||
|
||||
unsigned NumInlineSequences;
|
||||
llvm::AlignedCharArray<alignof(ImplicitConversionSequence),
|
||||
16 * sizeof(ImplicitConversionSequence)>
|
||||
InlineSpace;
|
||||
constexpr static unsigned NumInlineBytes =
|
||||
24 * sizeof(ImplicitConversionSequence);
|
||||
unsigned NumInlineBytesUsed;
|
||||
llvm::AlignedCharArray<alignof(void *), NumInlineBytes> InlineSpace;
|
||||
|
||||
/// If we have space, allocates from inline storage. Otherwise, allocates
|
||||
/// from the slab allocator.
|
||||
/// FIXME: It would probably be nice to have a SmallBumpPtrAllocator
|
||||
/// instead.
|
||||
template <typename T>
|
||||
T *slabAllocate(unsigned N) {
|
||||
// It's simpler if this doesn't need to consider alignment.
|
||||
static_assert(alignof(T) == alignof(void *),
|
||||
"Only works for pointer-aligned types.");
|
||||
static_assert(std::is_trivial<T>::value ||
|
||||
std::is_same<ImplicitConversionSequence, T>::value,
|
||||
"Add destruction logic to OverloadCandidateSet::clear().");
|
||||
|
||||
unsigned NBytes = sizeof(T) * N;
|
||||
if (NBytes > NumInlineBytes - NumInlineBytesUsed)
|
||||
return SlabAllocator.Allocate<T>(N);
|
||||
char *FreeSpaceStart = InlineSpace.buffer + NumInlineBytesUsed;
|
||||
assert(uintptr_t(FreeSpaceStart) % alignof(void *) == 0 &&
|
||||
"Misaligned storage!");
|
||||
|
||||
NumInlineBytesUsed += NBytes;
|
||||
return reinterpret_cast<T *>(FreeSpaceStart);
|
||||
}
|
||||
|
||||
OverloadCandidateSet(const OverloadCandidateSet &) = delete;
|
||||
void operator=(const OverloadCandidateSet &) = delete;
|
||||
@ -747,12 +803,17 @@ namespace clang {
|
||||
|
||||
public:
|
||||
OverloadCandidateSet(SourceLocation Loc, CandidateSetKind CSK)
|
||||
: Loc(Loc), Kind(CSK), NumInlineSequences(0) {}
|
||||
: Loc(Loc), Kind(CSK), NumInlineBytesUsed(0) {}
|
||||
~OverloadCandidateSet() { destroyCandidates(); }
|
||||
|
||||
SourceLocation getLocation() const { return Loc; }
|
||||
CandidateSetKind getKind() const { return Kind; }
|
||||
|
||||
/// Make a DiagnoseIfAttr* array in a block of memory that will live for
|
||||
/// as long as this OverloadCandidateSet. Returns a pointer to the start
|
||||
/// of that array.
|
||||
DiagnoseIfAttr **addDiagnoseIfComplaints(ArrayRef<DiagnoseIfAttr *> CA);
|
||||
|
||||
/// \brief Determine when this overload candidate will be new to the
|
||||
/// overload set.
|
||||
bool isNewCandidate(Decl *F) {
|
||||
@ -769,30 +830,32 @@ namespace clang {
|
||||
size_t size() const { return Candidates.size(); }
|
||||
bool empty() const { return Candidates.empty(); }
|
||||
|
||||
/// \brief Add a new candidate with NumConversions conversion sequence slots
|
||||
/// to the overload set.
|
||||
OverloadCandidate &addCandidate(unsigned NumConversions = 0) {
|
||||
Candidates.push_back(OverloadCandidate());
|
||||
OverloadCandidate &C = Candidates.back();
|
||||
|
||||
// Assign space from the inline array if there are enough free slots
|
||||
// available.
|
||||
if (NumConversions + NumInlineSequences <= 16) {
|
||||
ImplicitConversionSequence *I =
|
||||
(ImplicitConversionSequence *)InlineSpace.buffer;
|
||||
C.Conversions = &I[NumInlineSequences];
|
||||
NumInlineSequences += NumConversions;
|
||||
} else {
|
||||
// Otherwise get memory from the allocator.
|
||||
C.Conversions = ConversionSequenceAllocator
|
||||
.Allocate<ImplicitConversionSequence>(NumConversions);
|
||||
}
|
||||
/// \brief Allocate storage for conversion sequences for NumConversions
|
||||
/// conversions.
|
||||
ConversionSequenceList
|
||||
allocateConversionSequences(unsigned NumConversions) {
|
||||
ImplicitConversionSequence *Conversions =
|
||||
slabAllocate<ImplicitConversionSequence>(NumConversions);
|
||||
|
||||
// Construct the new objects.
|
||||
for (unsigned i = 0; i != NumConversions; ++i)
|
||||
new (&C.Conversions[i]) ImplicitConversionSequence();
|
||||
for (unsigned I = 0; I != NumConversions; ++I)
|
||||
new (&Conversions[I]) ImplicitConversionSequence();
|
||||
|
||||
C.NumConversions = NumConversions;
|
||||
return ConversionSequenceList(Conversions, NumConversions);
|
||||
}
|
||||
|
||||
/// \brief Add a new candidate with NumConversions conversion sequence slots
|
||||
/// to the overload set.
|
||||
OverloadCandidate &addCandidate(unsigned NumConversions = 0,
|
||||
ConversionSequenceList Conversions = None) {
|
||||
assert((Conversions.empty() || Conversions.size() == NumConversions) &&
|
||||
"preallocated conversion sequence has wrong length");
|
||||
|
||||
Candidates.push_back(OverloadCandidate());
|
||||
OverloadCandidate &C = Candidates.back();
|
||||
C.Conversions = Conversions.empty()
|
||||
? allocateConversionSequences(NumConversions)
|
||||
: Conversions;
|
||||
return C;
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "clang/AST/NSAPI.h"
|
||||
#include "clang/AST/PrettyPrinter.h"
|
||||
#include "clang/AST/TypeLoc.h"
|
||||
#include "clang/AST/TypeOrdering.h"
|
||||
#include "clang/Basic/ExpressionTraits.h"
|
||||
#include "clang/Basic/LangOptions.h"
|
||||
#include "clang/Basic/Module.h"
|
||||
@ -119,6 +120,7 @@ namespace clang {
|
||||
class FunctionProtoType;
|
||||
class FunctionTemplateDecl;
|
||||
class ImplicitConversionSequence;
|
||||
typedef MutableArrayRef<ImplicitConversionSequence> ConversionSequenceList;
|
||||
class InitListExpr;
|
||||
class InitializationKind;
|
||||
class InitializationSequence;
|
||||
@ -806,6 +808,12 @@ public:
|
||||
/// run time.
|
||||
Unevaluated,
|
||||
|
||||
/// \brief The current expression occurs within a braced-init-list within
|
||||
/// an unevaluated operand. This is mostly like a regular unevaluated
|
||||
/// context, except that we still instantiate constexpr functions that are
|
||||
/// referenced here so that we can perform narrowing checks correctly.
|
||||
UnevaluatedList,
|
||||
|
||||
/// \brief The current expression occurs within a discarded statement.
|
||||
/// This behaves largely similarly to an unevaluated operand in preventing
|
||||
/// definitions from being required, but not in other ways.
|
||||
@ -898,7 +906,8 @@ public:
|
||||
MangleNumberingContext &getMangleNumberingContext(ASTContext &Ctx);
|
||||
|
||||
bool isUnevaluated() const {
|
||||
return Context == Unevaluated || Context == UnevaluatedAbstract;
|
||||
return Context == Unevaluated || Context == UnevaluatedAbstract ||
|
||||
Context == UnevaluatedList;
|
||||
}
|
||||
};
|
||||
|
||||
@ -2510,10 +2519,11 @@ public:
|
||||
void AddOverloadCandidate(FunctionDecl *Function,
|
||||
DeclAccessPair FoundDecl,
|
||||
ArrayRef<Expr *> Args,
|
||||
OverloadCandidateSet& CandidateSet,
|
||||
OverloadCandidateSet &CandidateSet,
|
||||
bool SuppressUserConversions = false,
|
||||
bool PartialOverloading = false,
|
||||
bool AllowExplicit = false);
|
||||
bool AllowExplicit = false,
|
||||
ConversionSequenceList EarlyConversions = None);
|
||||
void AddFunctionCandidates(const UnresolvedSetImpl &Functions,
|
||||
ArrayRef<Expr *> Args,
|
||||
OverloadCandidateSet &CandidateSet,
|
||||
@ -2523,23 +2533,25 @@ public:
|
||||
void AddMethodCandidate(DeclAccessPair FoundDecl,
|
||||
QualType ObjectType,
|
||||
Expr::Classification ObjectClassification,
|
||||
ArrayRef<Expr *> Args,
|
||||
Expr *ThisArg, ArrayRef<Expr *> Args,
|
||||
OverloadCandidateSet& CandidateSet,
|
||||
bool SuppressUserConversion = false);
|
||||
void AddMethodCandidate(CXXMethodDecl *Method,
|
||||
DeclAccessPair FoundDecl,
|
||||
CXXRecordDecl *ActingContext, QualType ObjectType,
|
||||
Expr::Classification ObjectClassification,
|
||||
ArrayRef<Expr *> Args,
|
||||
Expr *ThisArg, ArrayRef<Expr *> Args,
|
||||
OverloadCandidateSet& CandidateSet,
|
||||
bool SuppressUserConversions = false,
|
||||
bool PartialOverloading = false);
|
||||
bool PartialOverloading = false,
|
||||
ConversionSequenceList EarlyConversions = None);
|
||||
void AddMethodTemplateCandidate(FunctionTemplateDecl *MethodTmpl,
|
||||
DeclAccessPair FoundDecl,
|
||||
CXXRecordDecl *ActingContext,
|
||||
TemplateArgumentListInfo *ExplicitTemplateArgs,
|
||||
QualType ObjectType,
|
||||
Expr::Classification ObjectClassification,
|
||||
Expr *ThisArg,
|
||||
ArrayRef<Expr *> Args,
|
||||
OverloadCandidateSet& CandidateSet,
|
||||
bool SuppressUserConversions = false,
|
||||
@ -2551,6 +2563,16 @@ public:
|
||||
OverloadCandidateSet& CandidateSet,
|
||||
bool SuppressUserConversions = false,
|
||||
bool PartialOverloading = false);
|
||||
bool CheckNonDependentConversions(FunctionTemplateDecl *FunctionTemplate,
|
||||
ArrayRef<QualType> ParamTypes,
|
||||
ArrayRef<Expr *> Args,
|
||||
OverloadCandidateSet &CandidateSet,
|
||||
ConversionSequenceList &Conversions,
|
||||
bool SuppressUserConversions,
|
||||
CXXRecordDecl *ActingContext = nullptr,
|
||||
QualType ObjectType = QualType(),
|
||||
Expr::Classification
|
||||
ObjectClassification = {});
|
||||
void AddConversionCandidate(CXXConversionDecl *Conversion,
|
||||
DeclAccessPair FoundDecl,
|
||||
CXXRecordDecl *ActingContext,
|
||||
@ -2603,6 +2625,38 @@ public:
|
||||
EnableIfAttr *CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
|
||||
bool MissingImplicitThis = false);
|
||||
|
||||
/// Check the diagnose_if attributes on the given function. Returns the
|
||||
/// first succesful fatal attribute, or null if calling Function(Args) isn't
|
||||
/// an error.
|
||||
///
|
||||
/// This only considers ArgDependent DiagnoseIfAttrs.
|
||||
///
|
||||
/// This will populate Nonfatal with all non-error DiagnoseIfAttrs that
|
||||
/// succeed. If this function returns non-null, the contents of Nonfatal are
|
||||
/// unspecified.
|
||||
DiagnoseIfAttr *
|
||||
checkArgDependentDiagnoseIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
|
||||
SmallVectorImpl<DiagnoseIfAttr *> &Nonfatal,
|
||||
bool MissingImplicitThis = false,
|
||||
Expr *ThisArg = nullptr);
|
||||
|
||||
/// Check the diagnose_if expressions on the given function. Returns the
|
||||
/// first succesful fatal attribute, or null if using Function isn't
|
||||
/// an error.
|
||||
///
|
||||
/// This ignores all ArgDependent DiagnoseIfAttrs.
|
||||
///
|
||||
/// This will populate Nonfatal with all non-error DiagnoseIfAttrs that
|
||||
/// succeed. If this function returns non-null, the contents of Nonfatal are
|
||||
/// unspecified.
|
||||
DiagnoseIfAttr *
|
||||
checkArgIndependentDiagnoseIf(FunctionDecl *Function,
|
||||
SmallVectorImpl<DiagnoseIfAttr *> &Nonfatal);
|
||||
|
||||
/// Emits the diagnostic contained in the given DiagnoseIfAttr at Loc. Also
|
||||
/// emits a note about the location of said attribute.
|
||||
void emitDiagnoseIfDiagnostic(SourceLocation Loc, const DiagnoseIfAttr *DIA);
|
||||
|
||||
/// Returns whether the given function's address can be taken or not,
|
||||
/// optionally emitting a diagnostic if the address can't be taken.
|
||||
///
|
||||
@ -3801,6 +3855,9 @@ public:
|
||||
/// variable will have in the given scope.
|
||||
QualType getCapturedDeclRefType(VarDecl *Var, SourceLocation Loc);
|
||||
|
||||
/// Mark all of the declarations referenced within a particular AST node as
|
||||
/// referenced. Used when template instantiation instantiates a non-dependent
|
||||
/// type -- entities referenced by the type are now referenced.
|
||||
void MarkDeclarationsReferencedInType(SourceLocation Loc, QualType T);
|
||||
void MarkDeclarationsReferencedInExpr(Expr *E,
|
||||
bool SkipLocalVariables = false);
|
||||
@ -6580,6 +6637,8 @@ public:
|
||||
/// \brief The explicitly-specified template arguments were not valid
|
||||
/// template arguments for the given template.
|
||||
TDK_InvalidExplicitArguments,
|
||||
/// \brief Checking non-dependent argument conversions failed.
|
||||
TDK_NonDependentConversionFailure,
|
||||
/// \brief Deduction failed; that's all we know.
|
||||
TDK_MiscellaneousDeductionFailure,
|
||||
/// \brief CUDA Target attributes do not match.
|
||||
@ -6618,22 +6677,21 @@ public:
|
||||
QualType OriginalArgType;
|
||||
};
|
||||
|
||||
TemplateDeductionResult
|
||||
FinishTemplateArgumentDeduction(FunctionTemplateDecl *FunctionTemplate,
|
||||
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
|
||||
unsigned NumExplicitlySpecified,
|
||||
FunctionDecl *&Specialization,
|
||||
sema::TemplateDeductionInfo &Info,
|
||||
SmallVectorImpl<OriginalCallArg> const *OriginalCallArgs = nullptr,
|
||||
bool PartialOverloading = false);
|
||||
TemplateDeductionResult FinishTemplateArgumentDeduction(
|
||||
FunctionTemplateDecl *FunctionTemplate,
|
||||
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
|
||||
unsigned NumExplicitlySpecified, FunctionDecl *&Specialization,
|
||||
sema::TemplateDeductionInfo &Info,
|
||||
SmallVectorImpl<OriginalCallArg> const *OriginalCallArgs = nullptr,
|
||||
bool PartialOverloading = false,
|
||||
llvm::function_ref<bool()> CheckNonDependent = []{ return false; });
|
||||
|
||||
TemplateDeductionResult
|
||||
DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate,
|
||||
TemplateArgumentListInfo *ExplicitTemplateArgs,
|
||||
ArrayRef<Expr *> Args,
|
||||
FunctionDecl *&Specialization,
|
||||
sema::TemplateDeductionInfo &Info,
|
||||
bool PartialOverloading = false);
|
||||
TemplateDeductionResult DeduceTemplateArguments(
|
||||
FunctionTemplateDecl *FunctionTemplate,
|
||||
TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
|
||||
FunctionDecl *&Specialization, sema::TemplateDeductionInfo &Info,
|
||||
bool PartialOverloading,
|
||||
llvm::function_ref<bool(ArrayRef<QualType>)> CheckNonDependent);
|
||||
|
||||
TemplateDeductionResult
|
||||
DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate,
|
||||
@ -6877,6 +6935,10 @@ public:
|
||||
/// Specializations whose definitions are currently being instantiated.
|
||||
llvm::DenseSet<std::pair<Decl *, unsigned>> InstantiatingSpecializations;
|
||||
|
||||
/// Non-dependent types used in templates that have already been instantiated
|
||||
/// by some template instantiation.
|
||||
llvm::DenseSet<QualType> InstantiatedNonDependentTypes;
|
||||
|
||||
/// \brief Extra modules inspected when performing a lookup during a template
|
||||
/// instantiation. Computed lazily.
|
||||
SmallVector<Module*, 16> ActiveTemplateInstantiationLookupModules;
|
||||
@ -10186,6 +10248,22 @@ public:
|
||||
IsDecltype);
|
||||
}
|
||||
|
||||
enum InitListTag { InitList };
|
||||
EnterExpressionEvaluationContext(Sema &Actions, InitListTag,
|
||||
bool ShouldEnter = true)
|
||||
: Actions(Actions), Entered(false) {
|
||||
// In C++11 onwards, narrowing checks are performed on the contents of
|
||||
// braced-init-lists, even when they occur within unevaluated operands.
|
||||
// Therefore we still need to instantiate constexpr functions used in such
|
||||
// a context.
|
||||
if (ShouldEnter && Actions.isUnevaluatedContext() &&
|
||||
Actions.getLangOpts().CPlusPlus11) {
|
||||
Actions.PushExpressionEvaluationContext(Sema::UnevaluatedList, nullptr,
|
||||
false);
|
||||
Entered = true;
|
||||
}
|
||||
}
|
||||
|
||||
~EnterExpressionEvaluationContext() {
|
||||
if (Entered)
|
||||
Actions.PopExpressionEvaluationContext();
|
||||
|
@ -278,6 +278,14 @@ def VirtualCallChecker : Checker<"VirtualCall">,
|
||||
|
||||
} // end: "optin.cplusplus"
|
||||
|
||||
let ParentPackage = CplusplusAlpha in {
|
||||
|
||||
def IteratorPastEndChecker : Checker<"IteratorPastEnd">,
|
||||
HelpText<"Check iterators used past end">,
|
||||
DescFile<"IteratorPastEndChecker.cpp">;
|
||||
|
||||
} // end: "alpha.cplusplus"
|
||||
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Valist checkers.
|
||||
|
@ -4543,6 +4543,12 @@ public:
|
||||
Call.getLValueBase().dyn_cast<const ValueDecl*>());
|
||||
if (!FD)
|
||||
return Error(Callee);
|
||||
// Don't call function pointers which have been cast to some other type.
|
||||
// Per DR (no number yet), the caller and callee can differ in noexcept.
|
||||
if (!Info.Ctx.hasSameFunctionTypeIgnoringExceptionSpec(
|
||||
CalleeType->getPointeeType(), FD->getType())) {
|
||||
return Error(E);
|
||||
}
|
||||
|
||||
// Overloaded operator calls to member functions are represented as normal
|
||||
// calls with '*this' as the first argument.
|
||||
@ -4558,14 +4564,42 @@ public:
|
||||
return false;
|
||||
This = &ThisVal;
|
||||
Args = Args.slice(1);
|
||||
} else if (MD && MD->isLambdaStaticInvoker()) {
|
||||
// Map the static invoker for the lambda back to the call operator.
|
||||
// Conveniently, we don't have to slice out the 'this' argument (as is
|
||||
// being done for the non-static case), since a static member function
|
||||
// doesn't have an implicit argument passed in.
|
||||
const CXXRecordDecl *ClosureClass = MD->getParent();
|
||||
assert(
|
||||
ClosureClass->captures_begin() == ClosureClass->captures_end() &&
|
||||
"Number of captures must be zero for conversion to function-ptr");
|
||||
|
||||
const CXXMethodDecl *LambdaCallOp =
|
||||
ClosureClass->getLambdaCallOperator();
|
||||
|
||||
// Set 'FD', the function that will be called below, to the call
|
||||
// operator. If the closure object represents a generic lambda, find
|
||||
// the corresponding specialization of the call operator.
|
||||
|
||||
if (ClosureClass->isGenericLambda()) {
|
||||
assert(MD->isFunctionTemplateSpecialization() &&
|
||||
"A generic lambda's static-invoker function must be a "
|
||||
"template specialization");
|
||||
const TemplateArgumentList *TAL = MD->getTemplateSpecializationArgs();
|
||||
FunctionTemplateDecl *CallOpTemplate =
|
||||
LambdaCallOp->getDescribedFunctionTemplate();
|
||||
void *InsertPos = nullptr;
|
||||
FunctionDecl *CorrespondingCallOpSpecialization =
|
||||
CallOpTemplate->findSpecialization(TAL->asArray(), InsertPos);
|
||||
assert(CorrespondingCallOpSpecialization &&
|
||||
"We must always have a function call operator specialization "
|
||||
"that corresponds to our static invoker specialization");
|
||||
FD = cast<CXXMethodDecl>(CorrespondingCallOpSpecialization);
|
||||
} else
|
||||
FD = LambdaCallOp;
|
||||
}
|
||||
|
||||
// Don't call function pointers which have been cast to some other type.
|
||||
// Per DR (no number yet), the caller and callee can differ in noexcept.
|
||||
if (!Info.Ctx.hasSameFunctionTypeIgnoringExceptionSpec(
|
||||
CalleeType->getPointeeType(), FD->getType())) {
|
||||
return Error(E);
|
||||
}
|
||||
|
||||
} else
|
||||
return Error(E);
|
||||
|
||||
@ -5834,6 +5868,7 @@ namespace {
|
||||
bool VisitCXXConstructExpr(const CXXConstructExpr *E) {
|
||||
return VisitCXXConstructExpr(E, E->getType());
|
||||
}
|
||||
bool VisitLambdaExpr(const LambdaExpr *E);
|
||||
bool VisitCXXInheritedCtorInitExpr(const CXXInheritedCtorInitExpr *E);
|
||||
bool VisitCXXConstructExpr(const CXXConstructExpr *E, QualType T);
|
||||
bool VisitCXXStdInitializerListExpr(const CXXStdInitializerListExpr *E);
|
||||
@ -6168,6 +6203,21 @@ bool RecordExprEvaluator::VisitCXXStdInitializerListExpr(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RecordExprEvaluator::VisitLambdaExpr(const LambdaExpr *E) {
|
||||
const CXXRecordDecl *ClosureClass = E->getLambdaClass();
|
||||
if (ClosureClass->isInvalidDecl()) return false;
|
||||
|
||||
if (Info.checkingPotentialConstantExpression()) return true;
|
||||
if (E->capture_size()) {
|
||||
Info.FFDiag(E, diag::note_unimplemented_constexpr_lambda_feature_ast)
|
||||
<< "can not evaluate lambda expressions with captures";
|
||||
return false;
|
||||
}
|
||||
// FIXME: Implement captures.
|
||||
Result = APValue(APValue::UninitStruct(), /*NumBases*/0, /*NumFields*/0);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool EvaluateRecord(const Expr *E, const LValue &This,
|
||||
APValue &Result, EvalInfo &Info) {
|
||||
assert(E->isRValue() && E->getType()->isRecordType() &&
|
||||
@ -6217,6 +6267,9 @@ public:
|
||||
bool VisitCXXStdInitializerListExpr(const CXXStdInitializerListExpr *E) {
|
||||
return VisitConstructExpr(E);
|
||||
}
|
||||
bool VisitLambdaExpr(const LambdaExpr *E) {
|
||||
return VisitConstructExpr(E);
|
||||
}
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
@ -10357,10 +10410,25 @@ bool Expr::isCXX11ConstantExpr(const ASTContext &Ctx, APValue *Result,
|
||||
|
||||
bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx,
|
||||
const FunctionDecl *Callee,
|
||||
ArrayRef<const Expr*> Args) const {
|
||||
ArrayRef<const Expr*> Args,
|
||||
const Expr *This) const {
|
||||
Expr::EvalStatus Status;
|
||||
EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpressionUnevaluated);
|
||||
|
||||
LValue ThisVal;
|
||||
const LValue *ThisPtr = nullptr;
|
||||
if (This) {
|
||||
#ifndef NDEBUG
|
||||
auto *MD = dyn_cast<CXXMethodDecl>(Callee);
|
||||
assert(MD && "Don't provide `this` for non-methods.");
|
||||
assert(!MD->isStatic() && "Don't provide `this` for static methods.");
|
||||
#endif
|
||||
if (EvaluateObjectArgument(Info, This, ThisVal))
|
||||
ThisPtr = &ThisVal;
|
||||
if (Info.EvalStatus.HasSideEffects)
|
||||
return false;
|
||||
}
|
||||
|
||||
ArgVector ArgValues(Args.size());
|
||||
for (ArrayRef<const Expr*>::iterator I = Args.begin(), E = Args.end();
|
||||
I != E; ++I) {
|
||||
@ -10373,7 +10441,7 @@ bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx,
|
||||
}
|
||||
|
||||
// Build fake call to Callee.
|
||||
CallStackFrame Frame(Info, Callee->getLocation(), Callee, /*This*/nullptr,
|
||||
CallStackFrame Frame(Info, Callee->getLocation(), Callee, ThisPtr,
|
||||
ArgValues.data());
|
||||
return Evaluate(Value, Info, this) && !Info.EvalStatus.HasSideEffects;
|
||||
}
|
||||
|
@ -109,13 +109,13 @@ static const DeclContext *getEffectiveParentContext(const DeclContext *DC) {
|
||||
|
||||
static const FunctionDecl *getStructor(const NamedDecl *ND) {
|
||||
if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(ND))
|
||||
return FTD->getTemplatedDecl();
|
||||
return FTD->getTemplatedDecl()->getCanonicalDecl();
|
||||
|
||||
const auto *FD = cast<FunctionDecl>(ND);
|
||||
if (const auto *FTD = FD->getPrimaryTemplate())
|
||||
return FTD->getTemplatedDecl();
|
||||
return FTD->getTemplatedDecl()->getCanonicalDecl();
|
||||
|
||||
return FD;
|
||||
return FD->getCanonicalDecl();
|
||||
}
|
||||
|
||||
/// MicrosoftMangleContextImpl - Overrides the default MangleContext for the
|
||||
@ -312,6 +312,10 @@ public:
|
||||
void mangleNestedName(const NamedDecl *ND);
|
||||
|
||||
private:
|
||||
bool isStructorDecl(const NamedDecl *ND) const {
|
||||
return ND == Structor || getStructor(ND) == Structor;
|
||||
}
|
||||
|
||||
void mangleUnqualifiedName(const NamedDecl *ND) {
|
||||
mangleUnqualifiedName(ND, ND->getDeclName());
|
||||
}
|
||||
@ -898,7 +902,7 @@ void MicrosoftCXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND,
|
||||
llvm_unreachable("Can't mangle Objective-C selector names here!");
|
||||
|
||||
case DeclarationName::CXXConstructorName:
|
||||
if (Structor == getStructor(ND)) {
|
||||
if (isStructorDecl(ND)) {
|
||||
if (StructorType == Ctor_CopyingClosure) {
|
||||
Out << "?_O";
|
||||
return;
|
||||
@ -912,7 +916,7 @@ void MicrosoftCXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND,
|
||||
return;
|
||||
|
||||
case DeclarationName::CXXDestructorName:
|
||||
if (ND == Structor)
|
||||
if (isStructorDecl(ND))
|
||||
// If the named decl is the C++ destructor we're mangling,
|
||||
// use the type we were given.
|
||||
mangleCXXDtorType(static_cast<CXXDtorType>(StructorType));
|
||||
@ -1862,7 +1866,7 @@ void MicrosoftCXXNameMangler::mangleFunctionType(const FunctionType *T,
|
||||
IsStructor = true;
|
||||
IsCtorClosure = (StructorType == Ctor_CopyingClosure ||
|
||||
StructorType == Ctor_DefaultClosure) &&
|
||||
getStructor(MD) == Structor;
|
||||
isStructorDecl(MD);
|
||||
if (IsCtorClosure)
|
||||
CC = getASTContext().getDefaultCallingConvention(
|
||||
/*IsVariadic=*/false, /*IsCXXMethod=*/true);
|
||||
@ -1883,7 +1887,7 @@ void MicrosoftCXXNameMangler::mangleFunctionType(const FunctionType *T,
|
||||
// <return-type> ::= <type>
|
||||
// ::= @ # structors (they have no declared return type)
|
||||
if (IsStructor) {
|
||||
if (isa<CXXDestructorDecl>(D) && D == Structor &&
|
||||
if (isa<CXXDestructorDecl>(D) && isStructorDecl(D) &&
|
||||
StructorType == Dtor_Deleting) {
|
||||
// The scalar deleting destructor takes an extra int argument.
|
||||
// However, the FunctionType generated has 0 arguments.
|
||||
|
@ -312,7 +312,8 @@ void EmitAssemblyHelper::CreatePasses(legacy::PassManager &MPM,
|
||||
// At O0 and O1 we only run the always inliner which is more efficient. At
|
||||
// higher optimization levels we run the normal inliner.
|
||||
if (CodeGenOpts.OptimizationLevel <= 1) {
|
||||
bool InsertLifetimeIntrinsics = CodeGenOpts.OptimizationLevel != 0;
|
||||
bool InsertLifetimeIntrinsics = (CodeGenOpts.OptimizationLevel != 0 &&
|
||||
!CodeGenOpts.DisableLifetimeMarkers);
|
||||
PMBuilder.Inliner = createAlwaysInlinerLegacyPass(InsertLifetimeIntrinsics);
|
||||
} else {
|
||||
PMBuilder.Inliner = createFunctionInliningPass(
|
||||
@ -519,11 +520,22 @@ void EmitAssemblyHelper::CreateTargetMachine(bool MustCreateTM) {
|
||||
.Case("dynamic-no-pic", llvm::Reloc::DynamicNoPIC);
|
||||
assert(RM.hasValue() && "invalid PIC model!");
|
||||
|
||||
CodeGenOpt::Level OptLevel = CodeGenOpt::Default;
|
||||
CodeGenOpt::Level OptLevel;
|
||||
switch (CodeGenOpts.OptimizationLevel) {
|
||||
default: break;
|
||||
case 0: OptLevel = CodeGenOpt::None; break;
|
||||
case 3: OptLevel = CodeGenOpt::Aggressive; break;
|
||||
default:
|
||||
llvm_unreachable("Invalid optimization level!");
|
||||
case 0:
|
||||
OptLevel = CodeGenOpt::None;
|
||||
break;
|
||||
case 1:
|
||||
OptLevel = CodeGenOpt::Less;
|
||||
break;
|
||||
case 2:
|
||||
OptLevel = CodeGenOpt::Default;
|
||||
break; // O2/Os/Oz
|
||||
case 3:
|
||||
OptLevel = CodeGenOpt::Aggressive;
|
||||
break;
|
||||
}
|
||||
|
||||
llvm::TargetOptions Options;
|
||||
@ -849,21 +861,8 @@ void EmitAssemblyHelper::EmitAssemblyWithNewPassManager(
|
||||
}
|
||||
}
|
||||
|
||||
static void runThinLTOBackend(const CodeGenOptions &CGOpts, Module *M,
|
||||
static void runThinLTOBackend(ModuleSummaryIndex *CombinedIndex, Module *M,
|
||||
std::unique_ptr<raw_pwrite_stream> OS) {
|
||||
// If we are performing a ThinLTO importing compile, load the function index
|
||||
// into memory and pass it into thinBackend, which will run the function
|
||||
// importer and invoke LTO passes.
|
||||
Expected<std::unique_ptr<ModuleSummaryIndex>> IndexOrErr =
|
||||
llvm::getModuleSummaryIndexForFile(CGOpts.ThinLTOIndexFile);
|
||||
if (!IndexOrErr) {
|
||||
logAllUnhandledErrors(IndexOrErr.takeError(), errs(),
|
||||
"Error loading index file '" +
|
||||
CGOpts.ThinLTOIndexFile + "': ");
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<ModuleSummaryIndex> CombinedIndex = std::move(*IndexOrErr);
|
||||
|
||||
StringMap<std::map<GlobalValue::GUID, GlobalValueSummary *>>
|
||||
ModuleToDefinedGVSummaries;
|
||||
CombinedIndex->collectDefinedGVSummariesPerModule(ModuleToDefinedGVSummaries);
|
||||
@ -949,8 +948,26 @@ void clang::EmitBackendOutput(DiagnosticsEngine &Diags,
|
||||
BackendAction Action,
|
||||
std::unique_ptr<raw_pwrite_stream> OS) {
|
||||
if (!CGOpts.ThinLTOIndexFile.empty()) {
|
||||
runThinLTOBackend(CGOpts, M, std::move(OS));
|
||||
return;
|
||||
// If we are performing a ThinLTO importing compile, load the function index
|
||||
// into memory and pass it into runThinLTOBackend, which will run the
|
||||
// function importer and invoke LTO passes.
|
||||
Expected<std::unique_ptr<ModuleSummaryIndex>> IndexOrErr =
|
||||
llvm::getModuleSummaryIndexForFile(CGOpts.ThinLTOIndexFile);
|
||||
if (!IndexOrErr) {
|
||||
logAllUnhandledErrors(IndexOrErr.takeError(), errs(),
|
||||
"Error loading index file '" +
|
||||
CGOpts.ThinLTOIndexFile + "': ");
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<ModuleSummaryIndex> CombinedIndex = std::move(*IndexOrErr);
|
||||
// A null CombinedIndex means we should skip ThinLTO compilation
|
||||
// (LLVM will optionally ignore empty index files, returning null instead
|
||||
// of an error).
|
||||
bool DoThinLTOBackend = CombinedIndex != nullptr;
|
||||
if (DoThinLTOBackend) {
|
||||
runThinLTOBackend(CombinedIndex.get(), M, std::move(OS));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
EmitAssemblyHelper AsmHelper(Diags, HeaderOpts, CGOpts, TOpts, LOpts, M);
|
||||
|
@ -616,6 +616,8 @@ struct EHPersonality {
|
||||
static const EHPersonality GNU_C_SJLJ;
|
||||
static const EHPersonality GNU_C_SEH;
|
||||
static const EHPersonality GNU_ObjC;
|
||||
static const EHPersonality GNU_ObjC_SJLJ;
|
||||
static const EHPersonality GNU_ObjC_SEH;
|
||||
static const EHPersonality GNUstep_ObjC;
|
||||
static const EHPersonality GNU_ObjCXX;
|
||||
static const EHPersonality NeXT_ObjC;
|
||||
|
@ -97,6 +97,10 @@ EHPersonality::GNU_CPlusPlus_SEH = { "__gxx_personality_seh0", nullptr };
|
||||
const EHPersonality
|
||||
EHPersonality::GNU_ObjC = {"__gnu_objc_personality_v0", "objc_exception_throw"};
|
||||
const EHPersonality
|
||||
EHPersonality::GNU_ObjC_SJLJ = {"__gnu_objc_personality_sj0", "objc_exception_throw"};
|
||||
const EHPersonality
|
||||
EHPersonality::GNU_ObjC_SEH = {"__gnu_objc_personality_seh0", "objc_exception_throw"};
|
||||
const EHPersonality
|
||||
EHPersonality::GNU_ObjCXX = { "__gnustep_objcxx_personality_v0", nullptr };
|
||||
const EHPersonality
|
||||
EHPersonality::GNUstep_ObjC = { "__gnustep_objc_personality_v0", nullptr };
|
||||
@ -137,6 +141,10 @@ static const EHPersonality &getObjCPersonality(const llvm::Triple &T,
|
||||
// fallthrough
|
||||
case ObjCRuntime::GCC:
|
||||
case ObjCRuntime::ObjFW:
|
||||
if (L.SjLjExceptions)
|
||||
return EHPersonality::GNU_ObjC_SJLJ;
|
||||
else if (useLibGCCSEHPersonality(T))
|
||||
return EHPersonality::GNU_ObjC_SEH;
|
||||
return EHPersonality::GNU_ObjC;
|
||||
}
|
||||
llvm_unreachable("bad runtime kind");
|
||||
|
@ -42,6 +42,9 @@ using namespace CodeGen;
|
||||
/// markers.
|
||||
static bool shouldEmitLifetimeMarkers(const CodeGenOptions &CGOpts,
|
||||
const LangOptions &LangOpts) {
|
||||
if (CGOpts.DisableLifetimeMarkers)
|
||||
return false;
|
||||
|
||||
// Asan uses markers for use-after-scope checks.
|
||||
if (CGOpts.SanitizeAddressUseAfterScope)
|
||||
return true;
|
||||
|
@ -3812,6 +3812,7 @@ ToolChain::CXXStdlibType NetBSD::GetDefaultCXXStdlibType() const {
|
||||
if (Major >= 7 || Major == 0) {
|
||||
switch (getArch()) {
|
||||
case llvm::Triple::aarch64:
|
||||
case llvm::Triple::aarch64_be:
|
||||
case llvm::Triple::arm:
|
||||
case llvm::Triple::armeb:
|
||||
case llvm::Triple::thumb:
|
||||
|
@ -9644,6 +9644,7 @@ void netbsd::Linker::ConstructJob(Compilation &C, const JobAction &JA,
|
||||
if (Major >= 7 || Major == 0) {
|
||||
switch (getToolChain().getArch()) {
|
||||
case llvm::Triple::aarch64:
|
||||
case llvm::Triple::aarch64_be:
|
||||
case llvm::Triple::arm:
|
||||
case llvm::Triple::armeb:
|
||||
case llvm::Triple::thumb:
|
||||
|
@ -1282,9 +1282,7 @@ private:
|
||||
return TT_UnaryOperator;
|
||||
|
||||
const FormatToken *NextToken = Tok.getNextNonComment();
|
||||
if (!NextToken ||
|
||||
NextToken->isOneOf(tok::arrow, Keywords.kw_final, tok::equal,
|
||||
Keywords.kw_override) ||
|
||||
if (!NextToken || NextToken->isOneOf(tok::arrow, tok::equal) ||
|
||||
(NextToken->is(tok::l_brace) && !NextToken->getNextNonComment()))
|
||||
return TT_PointerOrReference;
|
||||
|
||||
@ -2088,9 +2086,9 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
|
||||
!Line.IsMultiVariableDeclStmt)))
|
||||
return true;
|
||||
if (Left.is(TT_PointerOrReference))
|
||||
return Right.Tok.isLiteral() ||
|
||||
Right.isOneOf(TT_BlockComment, Keywords.kw_final,
|
||||
Keywords.kw_override) ||
|
||||
return Right.Tok.isLiteral() || Right.is(TT_BlockComment) ||
|
||||
(Right.isOneOf(Keywords.kw_override, Keywords.kw_final) &&
|
||||
!Right.is(TT_StartOfName)) ||
|
||||
(Right.is(tok::l_brace) && Right.BlockKind == BK_Block) ||
|
||||
(!Right.isOneOf(TT_PointerOrReference, TT_ArraySubscriptLSquare,
|
||||
tok::l_paren) &&
|
||||
|
@ -737,7 +737,7 @@ void UnwrappedLineParser::readTokenWithJavaScriptASI() {
|
||||
return;
|
||||
}
|
||||
if (Next->is(tok::exclaim) && PreviousMustBeValue)
|
||||
addUnwrappedLine();
|
||||
return addUnwrappedLine();
|
||||
bool NextMustBeValue = mustBeJSIdentOrValue(Keywords, Next);
|
||||
bool NextEndsTemplateExpr =
|
||||
Next->is(TT_TemplateString) && Next->TokenText.startswith("}");
|
||||
@ -745,9 +745,10 @@ void UnwrappedLineParser::readTokenWithJavaScriptASI() {
|
||||
(PreviousMustBeValue ||
|
||||
Previous->isOneOf(tok::r_square, tok::r_paren, tok::plusplus,
|
||||
tok::minusminus)))
|
||||
addUnwrappedLine();
|
||||
if (PreviousMustBeValue && isJSDeclOrStmt(Keywords, Next))
|
||||
addUnwrappedLine();
|
||||
return addUnwrappedLine();
|
||||
if ((PreviousMustBeValue || Previous->is(tok::r_brace)) &&
|
||||
isJSDeclOrStmt(Keywords, Next))
|
||||
return addUnwrappedLine();
|
||||
}
|
||||
|
||||
void UnwrappedLineParser::parseStructuralElement() {
|
||||
@ -1974,7 +1975,14 @@ void UnwrappedLineParser::parseJavaScriptEs6ImportExport() {
|
||||
!FormatTok->isStringLiteral())
|
||||
return;
|
||||
|
||||
while (!eof() && FormatTok->isNot(tok::semi)) {
|
||||
while (!eof()) {
|
||||
if (FormatTok->is(tok::semi))
|
||||
return;
|
||||
if (Line->Tokens.size() == 0) {
|
||||
// Common issue: Automatic Semicolon Insertion wrapped the line, so the
|
||||
// import statement should terminate.
|
||||
return;
|
||||
}
|
||||
if (FormatTok->is(tok::l_brace)) {
|
||||
FormatTok->BlockKind = BK_Block;
|
||||
parseBracedList();
|
||||
|
@ -520,6 +520,7 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
|
||||
Opts.EmitLLVMUseLists = A->getOption().getID() == OPT_emit_llvm_uselists;
|
||||
|
||||
Opts.DisableLLVMPasses = Args.hasArg(OPT_disable_llvm_passes);
|
||||
Opts.DisableLifetimeMarkers = Args.hasArg(OPT_disable_lifetimemarkers);
|
||||
Opts.DisableRedZone = Args.hasArg(OPT_disable_red_zone);
|
||||
Opts.ForbidGuardVariables = Args.hasArg(OPT_fforbid_guard_variables);
|
||||
Opts.UseRegisterSizedBitfieldAccess = Args.hasArg(
|
||||
|
@ -127,6 +127,12 @@ GeneratePCHAction::ComputeASTConsumerArguments(CompilerInstance &CI,
|
||||
return OS;
|
||||
}
|
||||
|
||||
bool GeneratePCHAction::BeginSourceFileAction(CompilerInstance &CI,
|
||||
StringRef Filename) {
|
||||
CI.getLangOpts().CompilingPCH = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<ASTConsumer>
|
||||
GenerateModuleAction::CreateASTConsumer(CompilerInstance &CI,
|
||||
StringRef InFile) {
|
||||
|
@ -53,6 +53,7 @@ SymbolInfo index::getSymbolInfo(const Decl *D) {
|
||||
assert(D);
|
||||
SymbolInfo Info;
|
||||
Info.Kind = SymbolKind::Unknown;
|
||||
Info.SubKind = SymbolSubKind::None;
|
||||
Info.Properties = SymbolPropertySet();
|
||||
Info.Lang = SymbolLanguage::C;
|
||||
|
||||
@ -183,10 +184,16 @@ SymbolInfo index::getSymbolInfo(const Decl *D) {
|
||||
Info.Kind = SymbolKind::NamespaceAlias;
|
||||
Info.Lang = SymbolLanguage::CXX;
|
||||
break;
|
||||
case Decl::CXXConstructor:
|
||||
case Decl::CXXConstructor: {
|
||||
Info.Kind = SymbolKind::Constructor;
|
||||
Info.Lang = SymbolLanguage::CXX;
|
||||
auto *CD = cast<CXXConstructorDecl>(D);
|
||||
if (CD->isCopyConstructor())
|
||||
Info.SubKind = SymbolSubKind::CXXCopyConstructor;
|
||||
else if (CD->isMoveConstructor())
|
||||
Info.SubKind = SymbolSubKind::CXXMoveConstructor;
|
||||
break;
|
||||
}
|
||||
case Decl::CXXDestructor:
|
||||
Info.Kind = SymbolKind::Destructor;
|
||||
Info.Lang = SymbolLanguage::CXX;
|
||||
@ -363,6 +370,15 @@ StringRef index::getSymbolKindString(SymbolKind K) {
|
||||
llvm_unreachable("invalid symbol kind");
|
||||
}
|
||||
|
||||
StringRef index::getSymbolSubKindString(SymbolSubKind K) {
|
||||
switch (K) {
|
||||
case SymbolSubKind::None: return "<none>";
|
||||
case SymbolSubKind::CXXCopyConstructor: return "cxx-copy-ctor";
|
||||
case SymbolSubKind::CXXMoveConstructor: return "cxx-move-ctor";
|
||||
}
|
||||
llvm_unreachable("invalid symbol subkind");
|
||||
}
|
||||
|
||||
StringRef index::getSymbolLanguageString(SymbolLanguage K) {
|
||||
switch (K) {
|
||||
case SymbolLanguage::C: return "C";
|
||||
|
@ -1996,10 +1996,12 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc,
|
||||
|
||||
// Ask HeaderInfo if we should enter this #include file. If not, #including
|
||||
// this file will have no effect.
|
||||
bool SkipHeader = false;
|
||||
if (ShouldEnter &&
|
||||
!HeaderInfo.ShouldEnterIncludeFile(*this, File, isImport,
|
||||
SuggestedModule.getModule())) {
|
||||
ShouldEnter = false;
|
||||
SkipHeader = true;
|
||||
if (Callbacks)
|
||||
Callbacks->FileSkipped(*File, FilenameTok, FileCharacter);
|
||||
}
|
||||
@ -2008,6 +2010,14 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc,
|
||||
if (!ShouldEnter) {
|
||||
// If this is a module import, make it visible if needed.
|
||||
if (auto *M = SuggestedModule.getModule()) {
|
||||
// When building a pch, -fmodule-name tells the compiler to textually
|
||||
// include headers in the specified module. But it is possible that
|
||||
// ShouldEnter is false because we are skipping the header. In that
|
||||
// case, We are not importing the specified module.
|
||||
if (SkipHeader && getLangOpts().CompilingPCH &&
|
||||
M->getTopLevelModuleName() == getLangOpts().CurrentModule)
|
||||
return;
|
||||
|
||||
makeModuleVisible(M, HashLoc);
|
||||
|
||||
if (IncludeTok.getIdentifierInfo()->getPPKeywordID() !=
|
||||
@ -2032,6 +2042,13 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc,
|
||||
|
||||
// Determine if we're switching to building a new submodule, and which one.
|
||||
if (auto *M = SuggestedModule.getModule()) {
|
||||
// When building a pch, -fmodule-name tells the compiler to textually
|
||||
// include headers in the specified module. We are not building the
|
||||
// specified module.
|
||||
if (getLangOpts().CompilingPCH &&
|
||||
M->getTopLevelModuleName() == getLangOpts().CurrentModule)
|
||||
return;
|
||||
|
||||
assert(!CurSubmodule && "should not have marked this as a module yet");
|
||||
CurSubmodule = M;
|
||||
|
||||
|
@ -306,10 +306,11 @@ unsigned Parser::ParseAttributeArgsCommon(
|
||||
|
||||
// Parse the non-empty comma-separated list of expressions.
|
||||
do {
|
||||
bool ShouldEnter = attributeParsedArgsUnevaluated(*AttrName);
|
||||
bool Uneval = attributeParsedArgsUnevaluated(*AttrName);
|
||||
EnterExpressionEvaluationContext Unevaluated(
|
||||
Actions, Sema::Unevaluated, /*LambdaContextDecl=*/nullptr,
|
||||
/*IsDecltype=*/false, ShouldEnter);
|
||||
Actions, Uneval ? Sema::Unevaluated : Sema::ConstantEvaluated,
|
||||
/*LambdaContextDecl=*/nullptr,
|
||||
/*IsDecltype=*/false);
|
||||
|
||||
ExprResult ArgExpr(
|
||||
Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()));
|
||||
|
@ -404,6 +404,10 @@ ExprResult Parser::ParseBraceInitializer() {
|
||||
return Actions.ActOnInitList(LBraceLoc, None, ConsumeBrace());
|
||||
}
|
||||
|
||||
// Enter an appropriate expression evaluation context for an initializer list.
|
||||
EnterExpressionEvaluationContext EnterContext(
|
||||
Actions, EnterExpressionEvaluationContext::InitList);
|
||||
|
||||
bool InitExprsOk = true;
|
||||
|
||||
while (1) {
|
||||
|
@ -1242,7 +1242,8 @@ bool Sema::CheckNeonBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
|
||||
QualType RHSTy = RHS.get()->getType();
|
||||
|
||||
llvm::Triple::ArchType Arch = Context.getTargetInfo().getTriple().getArch();
|
||||
bool IsPolyUnsigned = Arch == llvm::Triple::aarch64;
|
||||
bool IsPolyUnsigned = Arch == llvm::Triple::aarch64 ||
|
||||
Arch == llvm::Triple::aarch64_be;
|
||||
bool IsInt64Long =
|
||||
Context.getTargetInfo().getInt64Type() == TargetInfo::SignedLong;
|
||||
QualType EltTy =
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "clang/Sema/Lookup.h"
|
||||
#include "clang/Sema/Scope.h"
|
||||
#include "clang/Sema/SemaInternal.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/Support/MathExtras.h"
|
||||
|
||||
@ -890,34 +891,117 @@ static void handleLocksExcludedAttr(Sema &S, Decl *D,
|
||||
Attr.getAttributeSpellingListIndex()));
|
||||
}
|
||||
|
||||
static void handleEnableIfAttr(Sema &S, Decl *D, const AttributeList &Attr) {
|
||||
S.Diag(Attr.getLoc(), diag::ext_clang_enable_if);
|
||||
|
||||
Expr *Cond = Attr.getArgAsExpr(0);
|
||||
static bool checkFunctionConditionAttr(Sema &S, Decl *D,
|
||||
const AttributeList &Attr,
|
||||
Expr *&Cond, StringRef &Msg) {
|
||||
Cond = Attr.getArgAsExpr(0);
|
||||
if (!Cond->isTypeDependent()) {
|
||||
ExprResult Converted = S.PerformContextuallyConvertToBool(Cond);
|
||||
if (Converted.isInvalid())
|
||||
return;
|
||||
return false;
|
||||
Cond = Converted.get();
|
||||
}
|
||||
|
||||
StringRef Msg;
|
||||
if (!S.checkStringLiteralArgumentAttr(Attr, 1, Msg))
|
||||
return;
|
||||
return false;
|
||||
|
||||
if (Msg.empty())
|
||||
Msg = "<no message provided>";
|
||||
|
||||
SmallVector<PartialDiagnosticAt, 8> Diags;
|
||||
if (!Cond->isValueDependent() &&
|
||||
!Expr::isPotentialConstantExprUnevaluated(Cond, cast<FunctionDecl>(D),
|
||||
Diags)) {
|
||||
S.Diag(Attr.getLoc(), diag::err_enable_if_never_constant_expr);
|
||||
S.Diag(Attr.getLoc(), diag::err_attr_cond_never_constant_expr)
|
||||
<< Attr.getName();
|
||||
for (const PartialDiagnosticAt &PDiag : Diags)
|
||||
S.Diag(PDiag.first, PDiag.second);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void handleEnableIfAttr(Sema &S, Decl *D, const AttributeList &Attr) {
|
||||
S.Diag(Attr.getLoc(), diag::ext_clang_enable_if);
|
||||
|
||||
Expr *Cond;
|
||||
StringRef Msg;
|
||||
if (checkFunctionConditionAttr(S, D, Attr, Cond, Msg))
|
||||
D->addAttr(::new (S.Context)
|
||||
EnableIfAttr(Attr.getRange(), S.Context, Cond, Msg,
|
||||
Attr.getAttributeSpellingListIndex()));
|
||||
}
|
||||
|
||||
namespace {
|
||||
/// Determines if a given Expr references any of the given function's
|
||||
/// ParmVarDecls, or the function's implicit `this` parameter (if applicable).
|
||||
class ArgumentDependenceChecker
|
||||
: public RecursiveASTVisitor<ArgumentDependenceChecker> {
|
||||
#ifndef NDEBUG
|
||||
const CXXRecordDecl *ClassType;
|
||||
#endif
|
||||
llvm::SmallPtrSet<const ParmVarDecl *, 16> Parms;
|
||||
bool Result;
|
||||
|
||||
public:
|
||||
ArgumentDependenceChecker(const FunctionDecl *FD) {
|
||||
#ifndef NDEBUG
|
||||
if (const auto *MD = dyn_cast<CXXMethodDecl>(FD))
|
||||
ClassType = MD->getParent();
|
||||
else
|
||||
ClassType = nullptr;
|
||||
#endif
|
||||
Parms.insert(FD->param_begin(), FD->param_end());
|
||||
}
|
||||
|
||||
bool referencesArgs(Expr *E) {
|
||||
Result = false;
|
||||
TraverseStmt(E);
|
||||
return Result;
|
||||
}
|
||||
|
||||
bool VisitCXXThisExpr(CXXThisExpr *E) {
|
||||
assert(E->getType()->getPointeeCXXRecordDecl() == ClassType &&
|
||||
"`this` doesn't refer to the enclosing class?");
|
||||
Result = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VisitDeclRefExpr(DeclRefExpr *DRE) {
|
||||
if (const auto *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl()))
|
||||
if (Parms.count(PVD)) {
|
||||
Result = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static void handleDiagnoseIfAttr(Sema &S, Decl *D, const AttributeList &Attr) {
|
||||
S.Diag(Attr.getLoc(), diag::ext_clang_diagnose_if);
|
||||
|
||||
Expr *Cond;
|
||||
StringRef Msg;
|
||||
if (!checkFunctionConditionAttr(S, D, Attr, Cond, Msg))
|
||||
return;
|
||||
|
||||
StringRef DiagTypeStr;
|
||||
if (!S.checkStringLiteralArgumentAttr(Attr, 2, DiagTypeStr))
|
||||
return;
|
||||
|
||||
DiagnoseIfAttr::DiagnosticType DiagType;
|
||||
if (!DiagnoseIfAttr::ConvertStrToDiagnosticType(DiagTypeStr, DiagType)) {
|
||||
S.Diag(Attr.getArgAsExpr(2)->getLocStart(),
|
||||
diag::err_diagnose_if_invalid_diagnostic_type);
|
||||
return;
|
||||
}
|
||||
|
||||
D->addAttr(::new (S.Context)
|
||||
EnableIfAttr(Attr.getRange(), S.Context, Cond, Msg,
|
||||
Attr.getAttributeSpellingListIndex()));
|
||||
auto *FD = cast<FunctionDecl>(D);
|
||||
bool ArgDependent = ArgumentDependenceChecker(FD).referencesArgs(Cond);
|
||||
D->addAttr(::new (S.Context) DiagnoseIfAttr(
|
||||
Attr.getRange(), S.Context, Cond, Msg, DiagType, ArgDependent, FD,
|
||||
Attr.getAttributeSpellingListIndex()));
|
||||
}
|
||||
|
||||
static void handlePassObjectSizeAttr(Sema &S, Decl *D,
|
||||
@ -5682,6 +5766,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
|
||||
case AttributeList::AT_EnableIf:
|
||||
handleEnableIfAttr(S, D, Attr);
|
||||
break;
|
||||
case AttributeList::AT_DiagnoseIf:
|
||||
handleDiagnoseIfAttr(S, D, Attr);
|
||||
break;
|
||||
case AttributeList::AT_ExtVectorType:
|
||||
handleExtVectorTypeAttr(S, scope, D, Attr);
|
||||
break;
|
||||
|
@ -5395,13 +5395,31 @@ static void ReferenceDllExportedMethods(Sema &S, CXXRecordDecl *Class) {
|
||||
}
|
||||
}
|
||||
|
||||
static void checkForMultipleExportedDefaultConstructors(Sema &S, CXXRecordDecl *Class) {
|
||||
static void checkForMultipleExportedDefaultConstructors(Sema &S,
|
||||
CXXRecordDecl *Class) {
|
||||
// Only the MS ABI has default constructor closures, so we don't need to do
|
||||
// this semantic checking anywhere else.
|
||||
if (!S.Context.getTargetInfo().getCXXABI().isMicrosoft())
|
||||
return;
|
||||
|
||||
CXXConstructorDecl *LastExportedDefaultCtor = nullptr;
|
||||
for (Decl *Member : Class->decls()) {
|
||||
// Look for exported default constructors.
|
||||
auto *CD = dyn_cast<CXXConstructorDecl>(Member);
|
||||
if (!CD || !CD->isDefaultConstructor() || !CD->hasAttr<DLLExportAttr>())
|
||||
if (!CD || !CD->isDefaultConstructor())
|
||||
continue;
|
||||
auto *Attr = CD->getAttr<DLLExportAttr>();
|
||||
if (!Attr)
|
||||
continue;
|
||||
|
||||
// If the class is non-dependent, mark the default arguments as ODR-used so
|
||||
// that we can properly codegen the constructor closure.
|
||||
if (!Class->isDependentContext()) {
|
||||
for (ParmVarDecl *PD : CD->parameters()) {
|
||||
(void)S.CheckCXXDefaultArgExpr(Attr->getLocation(), CD, PD);
|
||||
S.DiscardCleanupsInEvaluationContext();
|
||||
}
|
||||
}
|
||||
|
||||
if (LastExportedDefaultCtor) {
|
||||
S.Diag(LastExportedDefaultCtor->getLocation(),
|
||||
@ -9135,6 +9153,16 @@ NamedDecl *Sema::BuildUsingDeclaration(Scope *S, AccessSpecifier AS,
|
||||
// invalid).
|
||||
if (R.empty() &&
|
||||
NameInfo.getName().getNameKind() != DeclarationName::CXXConstructorName) {
|
||||
// HACK: Work around a bug in libstdc++'s detection of ::gets. Sometimes
|
||||
// it will believe that glibc provides a ::gets in cases where it does not,
|
||||
// and will try to pull it into namespace std with a using-declaration.
|
||||
// Just ignore the using-declaration in that case.
|
||||
auto *II = NameInfo.getName().getAsIdentifierInfo();
|
||||
if (getLangOpts().CPlusPlus14 && II && II->isStr("gets") &&
|
||||
CurContext->isStdNamespace() &&
|
||||
isa<TranslationUnitDecl>(LookupContext) &&
|
||||
getSourceManager().isInSystemHeader(UsingLoc))
|
||||
return nullptr;
|
||||
if (TypoCorrection Corrected = CorrectTypo(
|
||||
R.getLookupNameInfo(), R.getLookupKind(), S, &SS,
|
||||
llvm::make_unique<UsingValidatorCCC>(
|
||||
@ -9828,9 +9856,14 @@ Sema::ComputeDefaultedDefaultCtorExceptionSpec(SourceLocation Loc,
|
||||
}
|
||||
|
||||
// Field constructors.
|
||||
for (const auto *F : ClassDecl->fields()) {
|
||||
for (auto *F : ClassDecl->fields()) {
|
||||
if (F->hasInClassInitializer()) {
|
||||
if (Expr *E = F->getInClassInitializer())
|
||||
Expr *E = F->getInClassInitializer();
|
||||
if (!E)
|
||||
// FIXME: It's a little wasteful to build and throw away a
|
||||
// CXXDefaultInitExpr here.
|
||||
E = BuildCXXDefaultInitExpr(Loc, F).get();
|
||||
if (E)
|
||||
ExceptSpec.CalledExpr(E);
|
||||
} else if (const RecordType *RecordTy
|
||||
= Context.getBaseElementType(F->getType())->getAs<RecordType>()) {
|
||||
@ -12291,6 +12324,10 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
|
||||
if (Field->getInClassInitializer())
|
||||
return CXXDefaultInitExpr::Create(Context, Loc, Field);
|
||||
|
||||
// If we might have already tried and failed to instantiate, don't try again.
|
||||
if (Field->isInvalidDecl())
|
||||
return ExprError();
|
||||
|
||||
// Maybe we haven't instantiated the in-class initializer. Go check the
|
||||
// pattern FieldDecl to see if it has one.
|
||||
CXXRecordDecl *ParentRD = cast<CXXRecordDecl>(Field->getParent());
|
||||
@ -12320,8 +12357,11 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
|
||||
}
|
||||
|
||||
if (InstantiateInClassInitializer(Loc, Field, Pattern,
|
||||
getTemplateInstantiationArgs(Field)))
|
||||
getTemplateInstantiationArgs(Field))) {
|
||||
// Don't diagnose this again.
|
||||
Field->setInvalidDecl();
|
||||
return ExprError();
|
||||
}
|
||||
return CXXDefaultInitExpr::Create(Context, Loc, Field);
|
||||
}
|
||||
|
||||
@ -12344,6 +12384,8 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
|
||||
<< OutermostClass << Field;
|
||||
Diag(Field->getLocEnd(), diag::note_in_class_initializer_not_yet_parsed);
|
||||
|
||||
// Don't diagnose this again.
|
||||
Field->setInvalidDecl();
|
||||
return ExprError();
|
||||
}
|
||||
|
||||
|
@ -342,6 +342,7 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc,
|
||||
}
|
||||
|
||||
// See if this is a deleted function.
|
||||
SmallVector<DiagnoseIfAttr *, 4> DiagnoseIfWarnings;
|
||||
if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
|
||||
if (FD->isDeleted()) {
|
||||
auto *Ctor = dyn_cast<CXXConstructorDecl>(FD);
|
||||
@ -363,6 +364,12 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc,
|
||||
|
||||
if (getLangOpts().CUDA && !CheckCUDACall(Loc, FD))
|
||||
return true;
|
||||
|
||||
if (const DiagnoseIfAttr *A =
|
||||
checkArgIndependentDiagnoseIf(FD, DiagnoseIfWarnings)) {
|
||||
emitDiagnoseIfDiagnostic(Loc, A);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// [OpenMP 4.0], 2.15 declare reduction Directive, Restrictions
|
||||
@ -377,6 +384,10 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc,
|
||||
Diag(D->getLocation(), diag::note_entity_declared_at) << D;
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const auto *W : DiagnoseIfWarnings)
|
||||
emitDiagnoseIfDiagnostic(Loc, W);
|
||||
|
||||
DiagnoseAvailabilityOfDecl(*this, D, Loc, UnknownObjCClass,
|
||||
ObjCPropertyAccess);
|
||||
|
||||
@ -5154,12 +5165,40 @@ static FunctionDecl *rewriteBuiltinFunctionDecl(Sema *Sema, ASTContext &Context,
|
||||
return OverloadDecl;
|
||||
}
|
||||
|
||||
static bool isNumberOfArgsValidForCall(Sema &S, const FunctionDecl *Callee,
|
||||
std::size_t NumArgs) {
|
||||
if (S.TooManyArguments(Callee->getNumParams(), NumArgs,
|
||||
/*PartialOverloading=*/false))
|
||||
return Callee->isVariadic();
|
||||
return Callee->getMinRequiredArguments() <= NumArgs;
|
||||
static void checkDirectCallValidity(Sema &S, const Expr *Fn,
|
||||
FunctionDecl *Callee,
|
||||
MultiExprArg ArgExprs) {
|
||||
// `Callee` (when called with ArgExprs) may be ill-formed. enable_if (and
|
||||
// similar attributes) really don't like it when functions are called with an
|
||||
// invalid number of args.
|
||||
if (S.TooManyArguments(Callee->getNumParams(), ArgExprs.size(),
|
||||
/*PartialOverloading=*/false) &&
|
||||
!Callee->isVariadic())
|
||||
return;
|
||||
if (Callee->getMinRequiredArguments() > ArgExprs.size())
|
||||
return;
|
||||
|
||||
if (const EnableIfAttr *Attr = S.CheckEnableIf(Callee, ArgExprs, true)) {
|
||||
S.Diag(Fn->getLocStart(),
|
||||
isa<CXXMethodDecl>(Callee)
|
||||
? diag::err_ovl_no_viable_member_function_in_call
|
||||
: diag::err_ovl_no_viable_function_in_call)
|
||||
<< Callee << Callee->getSourceRange();
|
||||
S.Diag(Callee->getLocation(),
|
||||
diag::note_ovl_candidate_disabled_by_function_cond_attr)
|
||||
<< Attr->getCond()->getSourceRange() << Attr->getMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
SmallVector<DiagnoseIfAttr *, 4> Nonfatal;
|
||||
if (const DiagnoseIfAttr *Attr = S.checkArgDependentDiagnoseIf(
|
||||
Callee, ArgExprs, Nonfatal, /*MissingImplicitThis=*/true)) {
|
||||
S.emitDiagnoseIfDiagnostic(Fn->getLocStart(), Attr);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto *W : Nonfatal)
|
||||
S.emitDiagnoseIfDiagnostic(Fn->getLocStart(), W);
|
||||
}
|
||||
|
||||
/// ActOnCallExpr - Handle a call to Fn with the specified array of arguments.
|
||||
@ -5294,26 +5333,8 @@ ExprResult Sema::ActOnCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc,
|
||||
|
||||
if (getLangOpts().OpenCL && checkOpenCLDisabledDecl(*FD, *Fn))
|
||||
return ExprError();
|
||||
|
||||
// CheckEnableIf assumes that the we're passing in a sane number of args for
|
||||
// FD, but that doesn't always hold true here. This is because, in some
|
||||
// cases, we'll emit a diag about an ill-formed function call, but then
|
||||
// we'll continue on as if the function call wasn't ill-formed. So, if the
|
||||
// number of args looks incorrect, don't do enable_if checks; we should've
|
||||
// already emitted an error about the bad call.
|
||||
if (FD->hasAttr<EnableIfAttr>() &&
|
||||
isNumberOfArgsValidForCall(*this, FD, ArgExprs.size())) {
|
||||
if (const EnableIfAttr *Attr = CheckEnableIf(FD, ArgExprs, true)) {
|
||||
Diag(Fn->getLocStart(),
|
||||
isa<CXXMethodDecl>(FD)
|
||||
? diag::err_ovl_no_viable_member_function_in_call
|
||||
: diag::err_ovl_no_viable_function_in_call)
|
||||
<< FD << FD->getSourceRange();
|
||||
Diag(FD->getLocation(),
|
||||
diag::note_ovl_candidate_disabled_by_enable_if_attr)
|
||||
<< Attr->getCond()->getSourceRange() << Attr->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
checkDirectCallValidity(*this, Fn, FD, ArgExprs);
|
||||
}
|
||||
|
||||
return BuildResolvedCallExpr(Fn, NDecl, LParenLoc, ArgExprs, RParenLoc,
|
||||
@ -13097,8 +13118,16 @@ void Sema::PopExpressionEvaluationContext() {
|
||||
// evaluate [...] a lambda-expression.
|
||||
D = diag::err_lambda_in_constant_expression;
|
||||
}
|
||||
for (const auto *L : Rec.Lambdas)
|
||||
Diag(L->getLocStart(), D);
|
||||
|
||||
// C++1z allows lambda expressions as core constant expressions.
|
||||
// FIXME: In C++1z, reinstate the restrictions on lambda expressions (CWG
|
||||
// 1607) from appearing within template-arguments and array-bounds that
|
||||
// are part of function-signatures. Be mindful that P0315 (Lambdas in
|
||||
// unevaluated contexts) might lift some of these restrictions in a
|
||||
// future version.
|
||||
if (Rec.Context != ConstantEvaluated || !getLangOpts().CPlusPlus1z)
|
||||
for (const auto *L : Rec.Lambdas)
|
||||
Diag(L->getLocStart(), D);
|
||||
} else {
|
||||
// Mark the capture expressions odr-used. This was deferred
|
||||
// during lambda expression creation.
|
||||
@ -13150,41 +13179,63 @@ ExprResult Sema::HandleExprEvaluationContextForTypeof(Expr *E) {
|
||||
return TransformToPotentiallyEvaluated(E);
|
||||
}
|
||||
|
||||
static bool IsPotentiallyEvaluatedContext(Sema &SemaRef) {
|
||||
// Do not mark anything as "used" within a dependent context; wait for
|
||||
// an instantiation.
|
||||
if (SemaRef.CurContext->isDependentContext())
|
||||
return false;
|
||||
|
||||
/// Are we within a context in which some evaluation could be performed (be it
|
||||
/// constant evaluation or runtime evaluation)? Sadly, this notion is not quite
|
||||
/// captured by C++'s idea of an "unevaluated context".
|
||||
static bool isEvaluatableContext(Sema &SemaRef) {
|
||||
switch (SemaRef.ExprEvalContexts.back().Context) {
|
||||
case Sema::Unevaluated:
|
||||
case Sema::UnevaluatedAbstract:
|
||||
// We are in an expression that is not potentially evaluated; do nothing.
|
||||
// (Depending on how you read the standard, we actually do need to do
|
||||
// something here for null pointer constants, but the standard's
|
||||
// definition of a null pointer constant is completely crazy.)
|
||||
return false;
|
||||
|
||||
case Sema::DiscardedStatement:
|
||||
// These are technically a potentially evaluated but they have the effect
|
||||
// of suppressing use marking.
|
||||
// Expressions in this context are never evaluated.
|
||||
return false;
|
||||
|
||||
case Sema::UnevaluatedList:
|
||||
case Sema::ConstantEvaluated:
|
||||
case Sema::PotentiallyEvaluated:
|
||||
// We are in a potentially evaluated expression (or a constant-expression
|
||||
// in C++03); we need to do implicit template instantiation, implicitly
|
||||
// define class members, and mark most declarations as used.
|
||||
// Expressions in this context could be evaluated.
|
||||
return true;
|
||||
|
||||
case Sema::PotentiallyEvaluatedIfUsed:
|
||||
// Referenced declarations will only be used if the construct in the
|
||||
// containing expression is used.
|
||||
// containing expression is used, at which point we'll be given another
|
||||
// turn to mark them.
|
||||
return false;
|
||||
}
|
||||
llvm_unreachable("Invalid context");
|
||||
}
|
||||
|
||||
/// Are we within a context in which references to resolved functions or to
|
||||
/// variables result in odr-use?
|
||||
static bool isOdrUseContext(Sema &SemaRef, bool SkipDependentUses = true) {
|
||||
// An expression in a template is not really an expression until it's been
|
||||
// instantiated, so it doesn't trigger odr-use.
|
||||
if (SkipDependentUses && SemaRef.CurContext->isDependentContext())
|
||||
return false;
|
||||
|
||||
switch (SemaRef.ExprEvalContexts.back().Context) {
|
||||
case Sema::Unevaluated:
|
||||
case Sema::UnevaluatedList:
|
||||
case Sema::UnevaluatedAbstract:
|
||||
case Sema::DiscardedStatement:
|
||||
return false;
|
||||
|
||||
case Sema::ConstantEvaluated:
|
||||
case Sema::PotentiallyEvaluated:
|
||||
return true;
|
||||
|
||||
case Sema::PotentiallyEvaluatedIfUsed:
|
||||
return false;
|
||||
}
|
||||
llvm_unreachable("Invalid context");
|
||||
}
|
||||
|
||||
static bool isImplicitlyDefinableConstexprFunction(FunctionDecl *Func) {
|
||||
CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Func);
|
||||
return Func->isConstexpr() &&
|
||||
(Func->isImplicitlyInstantiable() || (MD && !MD->isUserProvided()));
|
||||
}
|
||||
|
||||
/// \brief Mark a function referenced, and check whether it is odr-used
|
||||
/// (C++ [basic.def.odr]p2, C99 6.9p3)
|
||||
void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func,
|
||||
@ -13200,7 +13251,7 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func,
|
||||
//
|
||||
// We (incorrectly) mark overload resolution as an unevaluated context, so we
|
||||
// can just check that here.
|
||||
bool OdrUse = MightBeOdrUse && IsPotentiallyEvaluatedContext(*this);
|
||||
bool OdrUse = MightBeOdrUse && isOdrUseContext(*this);
|
||||
|
||||
// Determine whether we require a function definition to exist, per
|
||||
// C++11 [temp.inst]p3:
|
||||
@ -13209,27 +13260,11 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func,
|
||||
// specialization is implicitly instantiated when the specialization is
|
||||
// referenced in a context that requires a function definition to exist.
|
||||
//
|
||||
// We consider constexpr function templates to be referenced in a context
|
||||
// that requires a definition to exist whenever they are referenced.
|
||||
//
|
||||
// FIXME: This instantiates constexpr functions too frequently. If this is
|
||||
// really an unevaluated context (and we're not just in the definition of a
|
||||
// function template or overload resolution or other cases which we
|
||||
// incorrectly consider to be unevaluated contexts), and we're not in a
|
||||
// subexpression which we actually need to evaluate (for instance, a
|
||||
// template argument, array bound or an expression in a braced-init-list),
|
||||
// we are not permitted to instantiate this constexpr function definition.
|
||||
//
|
||||
// FIXME: This also implicitly defines special members too frequently. They
|
||||
// are only supposed to be implicitly defined if they are odr-used, but they
|
||||
// are not odr-used from constant expressions in unevaluated contexts.
|
||||
// However, they cannot be referenced if they are deleted, and they are
|
||||
// deleted whenever the implicit definition of the special member would
|
||||
// fail (with very few exceptions).
|
||||
CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Func);
|
||||
// That is either when this is an odr-use, or when a usage of a constexpr
|
||||
// function occurs within an evaluatable context.
|
||||
bool NeedDefinition =
|
||||
OdrUse || (Func->isConstexpr() && (Func->isImplicitlyInstantiable() ||
|
||||
(MD && !MD->isUserProvided())));
|
||||
OdrUse || (isEvaluatableContext(*this) &&
|
||||
isImplicitlyDefinableConstexprFunction(Func));
|
||||
|
||||
// C++14 [temp.expl.spec]p6:
|
||||
// If a template [...] is explicitly specialized then that specialization
|
||||
@ -14123,47 +14158,11 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc,
|
||||
Var->setReferenced();
|
||||
|
||||
TemplateSpecializationKind TSK = Var->getTemplateSpecializationKind();
|
||||
bool MarkODRUsed = true;
|
||||
|
||||
// If the context is not potentially evaluated, this is not an odr-use and
|
||||
// does not trigger instantiation.
|
||||
if (!IsPotentiallyEvaluatedContext(SemaRef)) {
|
||||
if (SemaRef.isUnevaluatedContext())
|
||||
return;
|
||||
|
||||
// If we don't yet know whether this context is going to end up being an
|
||||
// evaluated context, and we're referencing a variable from an enclosing
|
||||
// scope, add a potential capture.
|
||||
//
|
||||
// FIXME: Is this necessary? These contexts are only used for default
|
||||
// arguments, where local variables can't be used.
|
||||
const bool RefersToEnclosingScope =
|
||||
(SemaRef.CurContext != Var->getDeclContext() &&
|
||||
Var->getDeclContext()->isFunctionOrMethod() && Var->hasLocalStorage());
|
||||
if (RefersToEnclosingScope) {
|
||||
if (LambdaScopeInfo *const LSI =
|
||||
SemaRef.getCurLambda(/*IgnoreCapturedRegions=*/true)) {
|
||||
// If a variable could potentially be odr-used, defer marking it so
|
||||
// until we finish analyzing the full expression for any
|
||||
// lvalue-to-rvalue
|
||||
// or discarded value conversions that would obviate odr-use.
|
||||
// Add it to the list of potential captures that will be analyzed
|
||||
// later (ActOnFinishFullExpr) for eventual capture and odr-use marking
|
||||
// unless the variable is a reference that was initialized by a constant
|
||||
// expression (this will never need to be captured or odr-used).
|
||||
assert(E && "Capture variable should be used in an expression.");
|
||||
if (!Var->getType()->isReferenceType() ||
|
||||
!IsVariableNonDependentAndAConstantExpression(Var, SemaRef.Context))
|
||||
LSI->addPotentialCapture(E->IgnoreParens());
|
||||
}
|
||||
}
|
||||
|
||||
if (!isTemplateInstantiation(TSK))
|
||||
return;
|
||||
|
||||
// Instantiate, but do not mark as odr-used, variable templates.
|
||||
MarkODRUsed = false;
|
||||
}
|
||||
bool OdrUseContext = isOdrUseContext(SemaRef);
|
||||
bool NeedDefinition =
|
||||
OdrUseContext || (isEvaluatableContext(SemaRef) &&
|
||||
Var->isUsableInConstantExpressions(SemaRef.Context));
|
||||
|
||||
VarTemplateSpecializationDecl *VarSpec =
|
||||
dyn_cast<VarTemplateSpecializationDecl>(Var);
|
||||
@ -14173,14 +14172,15 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc,
|
||||
// If this might be a member specialization of a static data member, check
|
||||
// the specialization is visible. We already did the checks for variable
|
||||
// template specializations when we created them.
|
||||
if (TSK != TSK_Undeclared && !isa<VarTemplateSpecializationDecl>(Var))
|
||||
if (NeedDefinition && TSK != TSK_Undeclared &&
|
||||
!isa<VarTemplateSpecializationDecl>(Var))
|
||||
SemaRef.checkSpecializationVisibility(Loc, Var);
|
||||
|
||||
// Perform implicit instantiation of static data members, static data member
|
||||
// templates of class templates, and variable template specializations. Delay
|
||||
// instantiations of variable templates, except for those that could be used
|
||||
// in a constant expression.
|
||||
if (isTemplateInstantiation(TSK)) {
|
||||
if (NeedDefinition && isTemplateInstantiation(TSK)) {
|
||||
bool TryInstantiating = TSK == TSK_ImplicitInstantiation;
|
||||
|
||||
if (TryInstantiating && !isa<VarTemplateSpecializationDecl>(Var)) {
|
||||
@ -14219,9 +14219,6 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc,
|
||||
}
|
||||
}
|
||||
|
||||
if (!MarkODRUsed)
|
||||
return;
|
||||
|
||||
// Per C++11 [basic.def.odr], a variable is odr-used "unless it satisfies
|
||||
// the requirements for appearing in a constant expression (5.19) and, if
|
||||
// it is an object, the lvalue-to-rvalue conversion (4.1)
|
||||
@ -14230,14 +14227,41 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc,
|
||||
// Note that we use the C++11 definition everywhere because nothing in
|
||||
// C++03 depends on whether we get the C++03 version correct. The second
|
||||
// part does not apply to references, since they are not objects.
|
||||
if (E && IsVariableAConstantExpression(Var, SemaRef.Context)) {
|
||||
if (OdrUseContext && E &&
|
||||
IsVariableAConstantExpression(Var, SemaRef.Context)) {
|
||||
// A reference initialized by a constant expression can never be
|
||||
// odr-used, so simply ignore it.
|
||||
if (!Var->getType()->isReferenceType())
|
||||
SemaRef.MaybeODRUseExprs.insert(E);
|
||||
} else
|
||||
} else if (OdrUseContext) {
|
||||
MarkVarDeclODRUsed(Var, Loc, SemaRef,
|
||||
/*MaxFunctionScopeIndex ptr*/ nullptr);
|
||||
} else if (isOdrUseContext(SemaRef, /*SkipDependentUses*/false)) {
|
||||
// If this is a dependent context, we don't need to mark variables as
|
||||
// odr-used, but we may still need to track them for lambda capture.
|
||||
// FIXME: Do we also need to do this inside dependent typeid expressions
|
||||
// (which are modeled as unevaluated at this point)?
|
||||
const bool RefersToEnclosingScope =
|
||||
(SemaRef.CurContext != Var->getDeclContext() &&
|
||||
Var->getDeclContext()->isFunctionOrMethod() && Var->hasLocalStorage());
|
||||
if (RefersToEnclosingScope) {
|
||||
if (LambdaScopeInfo *const LSI =
|
||||
SemaRef.getCurLambda(/*IgnoreCapturedRegions=*/true)) {
|
||||
// If a variable could potentially be odr-used, defer marking it so
|
||||
// until we finish analyzing the full expression for any
|
||||
// lvalue-to-rvalue
|
||||
// or discarded value conversions that would obviate odr-use.
|
||||
// Add it to the list of potential captures that will be analyzed
|
||||
// later (ActOnFinishFullExpr) for eventual capture and odr-use marking
|
||||
// unless the variable is a reference that was initialized by a constant
|
||||
// expression (this will never need to be captured or odr-used).
|
||||
assert(E && "Capture variable should be used in an expression.");
|
||||
if (!Var->getType()->isReferenceType() ||
|
||||
!IsVariableNonDependentAndAConstantExpression(Var, SemaRef.Context))
|
||||
LSI->addPotentialCapture(E->IgnoreParens());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Mark a variable referenced, and check whether it is odr-used
|
||||
@ -14333,9 +14357,13 @@ void Sema::MarkAnyDeclReferenced(SourceLocation Loc, Decl *D,
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Mark all of the declarations referenced
|
||||
// Mark all of the declarations used by a type as referenced.
|
||||
// FIXME: Not fully implemented yet! We need to have a better understanding
|
||||
// of when we're entering
|
||||
// of when we're entering a context we should not recurse into.
|
||||
// FIXME: This is and EvaluatedExprMarker are more-or-less equivalent to
|
||||
// TreeTransforms rebuilding the type in a new context. Rather than
|
||||
// duplicating the TreeTransform logic, we should consider reusing it here.
|
||||
// Currently that causes problems when rebuilding LambdaExprs.
|
||||
class MarkReferencedDecls : public RecursiveASTVisitor<MarkReferencedDecls> {
|
||||
Sema &S;
|
||||
SourceLocation Loc;
|
||||
@ -14346,33 +14374,28 @@ namespace {
|
||||
MarkReferencedDecls(Sema &S, SourceLocation Loc) : S(S), Loc(Loc) { }
|
||||
|
||||
bool TraverseTemplateArgument(const TemplateArgument &Arg);
|
||||
bool TraverseRecordType(RecordType *T);
|
||||
};
|
||||
}
|
||||
|
||||
bool MarkReferencedDecls::TraverseTemplateArgument(
|
||||
const TemplateArgument &Arg) {
|
||||
if (Arg.getKind() == TemplateArgument::Declaration) {
|
||||
if (Decl *D = Arg.getAsDecl())
|
||||
S.MarkAnyDeclReferenced(Loc, D, true);
|
||||
{
|
||||
// A non-type template argument is a constant-evaluated context.
|
||||
EnterExpressionEvaluationContext Evaluated(S, Sema::ConstantEvaluated);
|
||||
if (Arg.getKind() == TemplateArgument::Declaration) {
|
||||
if (Decl *D = Arg.getAsDecl())
|
||||
S.MarkAnyDeclReferenced(Loc, D, true);
|
||||
} else if (Arg.getKind() == TemplateArgument::Expression) {
|
||||
S.MarkDeclarationsReferencedInExpr(Arg.getAsExpr(), false);
|
||||
}
|
||||
}
|
||||
|
||||
return Inherited::TraverseTemplateArgument(Arg);
|
||||
}
|
||||
|
||||
bool MarkReferencedDecls::TraverseRecordType(RecordType *T) {
|
||||
if (ClassTemplateSpecializationDecl *Spec
|
||||
= dyn_cast<ClassTemplateSpecializationDecl>(T->getDecl())) {
|
||||
const TemplateArgumentList &Args = Spec->getTemplateArgs();
|
||||
return TraverseTemplateArguments(Args.data(), Args.size());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Sema::MarkDeclarationsReferencedInType(SourceLocation Loc, QualType T) {
|
||||
MarkReferencedDecls Marker(*this, Loc);
|
||||
Marker.TraverseType(Context.getCanonicalType(T));
|
||||
Marker.TraverseType(T);
|
||||
}
|
||||
|
||||
namespace {
|
||||
@ -14479,6 +14502,7 @@ bool Sema::DiagRuntimeBehavior(SourceLocation Loc, const Stmt *Statement,
|
||||
const PartialDiagnostic &PD) {
|
||||
switch (ExprEvalContexts.back().Context) {
|
||||
case Unevaluated:
|
||||
case UnevaluatedList:
|
||||
case UnevaluatedAbstract:
|
||||
case DiscardedStatement:
|
||||
// The argument will never be evaluated, so don't complain.
|
||||
|
@ -134,6 +134,7 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef,
|
||||
assert(!AbstractInstanceResult);
|
||||
switch (SemaRef.ExprEvalContexts.back().Context) {
|
||||
case Sema::Unevaluated:
|
||||
case Sema::UnevaluatedList:
|
||||
if (isField && SemaRef.getLangOpts().CPlusPlus11)
|
||||
AbstractInstanceResult = IMA_Field_Uneval_Context;
|
||||
break;
|
||||
|
@ -6561,6 +6561,13 @@ InitializationSequence::Perform(Sema &S,
|
||||
break;
|
||||
}
|
||||
|
||||
// Promote from an unevaluated context to an unevaluated list context in
|
||||
// C++11 list-initialization; we need to instantiate entities usable in
|
||||
// constant expressions here in order to perform narrowing checks =(
|
||||
EnterExpressionEvaluationContext Evaluated(
|
||||
S, EnterExpressionEvaluationContext::InitList,
|
||||
CurInit.get() && isa<InitListExpr>(CurInit.get()));
|
||||
|
||||
// C++ [class.abstract]p2:
|
||||
// no objects of an abstract class can be created except as subobjects
|
||||
// of a class derived from it
|
||||
|
@ -1274,7 +1274,7 @@ static void addFunctionPointerConversion(Sema &S,
|
||||
ConvTy,
|
||||
ConvTSI,
|
||||
/*isInline=*/true, /*isExplicit=*/false,
|
||||
/*isConstexpr=*/false,
|
||||
/*isConstexpr=*/S.getLangOpts().CPlusPlus1z,
|
||||
CallOperator->getBody()->getLocEnd());
|
||||
Conversion->setAccess(AS_public);
|
||||
Conversion->setImplicit(true);
|
||||
@ -1565,6 +1565,7 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc,
|
||||
// A lambda-expression shall not appear in an unevaluated operand
|
||||
// (Clause 5).
|
||||
case Unevaluated:
|
||||
case UnevaluatedList:
|
||||
case UnevaluatedAbstract:
|
||||
// C++1y [expr.const]p2:
|
||||
// A conditional-expression e is a core constant expression unless the
|
||||
|
@ -2960,6 +2960,7 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *RD,
|
||||
if (CXXMethodDecl *M = dyn_cast<CXXMethodDecl>(Cand->getUnderlyingDecl())) {
|
||||
if (SM == CXXCopyAssignment || SM == CXXMoveAssignment)
|
||||
AddMethodCandidate(M, Cand, RD, ThisTy, Classification,
|
||||
/*ThisArg=*/nullptr,
|
||||
llvm::makeArrayRef(&Arg, NumArgs), OCS, true);
|
||||
else if (CtorInfo)
|
||||
AddOverloadCandidate(CtorInfo.Constructor, CtorInfo.FoundDecl,
|
||||
@ -2972,7 +2973,7 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *RD,
|
||||
if (SM == CXXCopyAssignment || SM == CXXMoveAssignment)
|
||||
AddMethodTemplateCandidate(
|
||||
Tmpl, Cand, RD, nullptr, ThisTy, Classification,
|
||||
llvm::makeArrayRef(&Arg, NumArgs), OCS, true);
|
||||
/*ThisArg=*/nullptr, llvm::makeArrayRef(&Arg, NumArgs), OCS, true);
|
||||
else if (CtorInfo)
|
||||
AddTemplateOverloadCandidate(
|
||||
CtorInfo.ConstructorTmpl, CtorInfo.FoundDecl, nullptr,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -5158,6 +5158,11 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
|
||||
return Arg;
|
||||
}
|
||||
|
||||
// The initialization of the parameter from the argument is
|
||||
// a constant-evaluated context.
|
||||
EnterExpressionEvaluationContext ConstantEvaluated(*this,
|
||||
Sema::ConstantEvaluated);
|
||||
|
||||
if (getLangOpts().CPlusPlus1z) {
|
||||
// C++1z [temp.arg.nontype]p1:
|
||||
// A template-argument for a non-type template parameter shall be
|
||||
|
@ -669,6 +669,19 @@ public:
|
||||
Info.PendingDeducedPacks[Pack.Index] = Pack.Outer;
|
||||
}
|
||||
|
||||
/// Determine whether this pack has already been partially expanded into a
|
||||
/// sequence of (prior) function parameters / template arguments.
|
||||
bool isPartiallyExpanded() {
|
||||
if (Packs.size() != 1 || !S.CurrentInstantiationScope)
|
||||
return false;
|
||||
|
||||
auto *PartiallySubstitutedPack =
|
||||
S.CurrentInstantiationScope->getPartiallySubstitutedPack();
|
||||
return PartiallySubstitutedPack &&
|
||||
getDepthAndIndex(PartiallySubstitutedPack) ==
|
||||
std::make_pair(Info.getDeducedDepth(), Packs.front().Index);
|
||||
}
|
||||
|
||||
/// Move to deducing the next element in each pack that is being deduced.
|
||||
void nextPackElement() {
|
||||
// Capture the deduced template arguments for each parameter pack expanded
|
||||
@ -2552,6 +2565,12 @@ static bool isSimpleTemplateIdType(QualType T) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
MarkUsedTemplateParameters(ASTContext &Ctx, QualType T,
|
||||
bool OnlyDeduced,
|
||||
unsigned Level,
|
||||
llvm::SmallBitVector &Deduced);
|
||||
|
||||
/// \brief Substitute the explicitly-provided template arguments into the
|
||||
/// given function template according to C++ [temp.arg.explicit].
|
||||
///
|
||||
@ -2613,7 +2632,7 @@ Sema::SubstituteExplicitTemplateArguments(
|
||||
// Enter a new template instantiation context where we check the
|
||||
// explicitly-specified template arguments against this function template,
|
||||
// and then substitute them into the function parameter types.
|
||||
SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(), Deduced.end());
|
||||
SmallVector<TemplateArgument, 4> DeducedArgs;
|
||||
InstantiatingTemplate Inst(*this, Info.getLocation(), FunctionTemplate,
|
||||
DeducedArgs,
|
||||
ActiveTemplateInstantiation::ExplicitTemplateArgumentSubstitution,
|
||||
@ -2893,14 +2912,13 @@ static unsigned getPackIndexForParam(Sema &S,
|
||||
///
|
||||
/// \param OriginalCallArgs If non-NULL, the original call arguments against
|
||||
/// which the deduced argument types should be compared.
|
||||
Sema::TemplateDeductionResult
|
||||
Sema::FinishTemplateArgumentDeduction(FunctionTemplateDecl *FunctionTemplate,
|
||||
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
|
||||
unsigned NumExplicitlySpecified,
|
||||
FunctionDecl *&Specialization,
|
||||
TemplateDeductionInfo &Info,
|
||||
SmallVectorImpl<OriginalCallArg> const *OriginalCallArgs,
|
||||
bool PartialOverloading) {
|
||||
Sema::TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
|
||||
FunctionTemplateDecl *FunctionTemplate,
|
||||
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
|
||||
unsigned NumExplicitlySpecified, FunctionDecl *&Specialization,
|
||||
TemplateDeductionInfo &Info,
|
||||
SmallVectorImpl<OriginalCallArg> const *OriginalCallArgs,
|
||||
bool PartialOverloading, llvm::function_ref<bool()> CheckNonDependent) {
|
||||
// Unevaluated SFINAE context.
|
||||
EnterExpressionEvaluationContext Unevaluated(*this, Sema::Unevaluated);
|
||||
SFINAETrap Trap(*this);
|
||||
@ -2927,6 +2945,18 @@ Sema::FinishTemplateArgumentDeduction(FunctionTemplateDecl *FunctionTemplate,
|
||||
PartialOverloading))
|
||||
return Result;
|
||||
|
||||
// C++ [temp.deduct.call]p10: [DR1391]
|
||||
// If deduction succeeds for all parameters that contain
|
||||
// template-parameters that participate in template argument deduction,
|
||||
// and all template arguments are explicitly specified, deduced, or
|
||||
// obtained from default template arguments, remaining parameters are then
|
||||
// compared with the corresponding arguments. For each remaining parameter
|
||||
// P with a type that was non-dependent before substitution of any
|
||||
// explicitly-specified template arguments, if the corresponding argument
|
||||
// A cannot be implicitly converted to P, deduction fails.
|
||||
if (CheckNonDependent())
|
||||
return TDK_NonDependentConversionFailure;
|
||||
|
||||
// Form the template argument list from the deduced template arguments.
|
||||
TemplateArgumentList *DeducedArgumentList
|
||||
= TemplateArgumentList::CreateCopy(Context, Builder);
|
||||
@ -3373,12 +3403,19 @@ static Sema::TemplateDeductionResult DeduceTemplateArgumentsFromCallArgument(
|
||||
/// \param Info the argument will be updated to provide additional information
|
||||
/// about template argument deduction.
|
||||
///
|
||||
/// \param CheckNonDependent A callback to invoke to check conversions for
|
||||
/// non-dependent parameters, between deduction and substitution, per DR1391.
|
||||
/// If this returns true, substitution will be skipped and we return
|
||||
/// TDK_NonDependentConversionFailure. The callback is passed the parameter
|
||||
/// types (after substituting explicit template arguments).
|
||||
///
|
||||
/// \returns the result of template argument deduction.
|
||||
Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
|
||||
FunctionTemplateDecl *FunctionTemplate,
|
||||
TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
|
||||
FunctionDecl *&Specialization, TemplateDeductionInfo &Info,
|
||||
bool PartialOverloading) {
|
||||
bool PartialOverloading,
|
||||
llvm::function_ref<bool(ArrayRef<QualType>)> CheckNonDependent) {
|
||||
if (FunctionTemplate->isInvalidDecl())
|
||||
return TDK_Invalid;
|
||||
|
||||
@ -3389,7 +3426,6 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
|
||||
// Template argument deduction is done by comparing each function template
|
||||
// parameter type (call it P) with the type of the corresponding argument
|
||||
// of the call (call it A) as described below.
|
||||
unsigned CheckArgs = Args.size();
|
||||
if (Args.size() < Function->getMinRequiredArguments() && !PartialOverloading)
|
||||
return TDK_TooFewArguments;
|
||||
else if (TooManyArguments(NumParams, Args.size(), PartialOverloading)) {
|
||||
@ -3397,9 +3433,7 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
|
||||
= Function->getType()->getAs<FunctionProtoType>();
|
||||
if (Proto->isTemplateVariadic())
|
||||
/* Do nothing */;
|
||||
else if (Proto->isVariadic())
|
||||
CheckArgs = NumParams;
|
||||
else
|
||||
else if (!Proto->isVariadic())
|
||||
return TDK_TooManyArguments;
|
||||
}
|
||||
|
||||
@ -3409,7 +3443,7 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
|
||||
TemplateParameterList *TemplateParams
|
||||
= FunctionTemplate->getTemplateParameters();
|
||||
SmallVector<DeducedTemplateArgument, 4> Deduced;
|
||||
SmallVector<QualType, 4> ParamTypes;
|
||||
SmallVector<QualType, 8> ParamTypes;
|
||||
unsigned NumExplicitlySpecified = 0;
|
||||
if (ExplicitTemplateArgs) {
|
||||
TemplateDeductionResult Result =
|
||||
@ -3429,7 +3463,7 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
|
||||
ParamTypes.push_back(Function->getParamDecl(I)->getType());
|
||||
}
|
||||
|
||||
SmallVector<OriginalCallArg, 4> OriginalCallArgs;
|
||||
SmallVector<OriginalCallArg, 8> OriginalCallArgs;
|
||||
|
||||
// Deduce an argument of type ParamType from an expression with index ArgIdx.
|
||||
auto DeduceCallArgument = [&](QualType ParamType, unsigned ArgIdx) {
|
||||
@ -3448,6 +3482,7 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
|
||||
|
||||
// Deduce template arguments from the function parameters.
|
||||
Deduced.resize(TemplateParams->size());
|
||||
SmallVector<QualType, 8> ParamTypesForArgChecking;
|
||||
for (unsigned ParamIdx = 0, NumParamTypes = ParamTypes.size(), ArgIdx = 0;
|
||||
ParamIdx != NumParamTypes; ++ParamIdx) {
|
||||
QualType ParamType = ParamTypes[ParamIdx];
|
||||
@ -3456,51 +3491,68 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
|
||||
dyn_cast<PackExpansionType>(ParamType);
|
||||
if (!ParamExpansion) {
|
||||
// Simple case: matching a function parameter to a function argument.
|
||||
if (ArgIdx >= CheckArgs)
|
||||
if (ArgIdx >= Args.size())
|
||||
break;
|
||||
|
||||
ParamTypesForArgChecking.push_back(ParamType);
|
||||
if (auto Result = DeduceCallArgument(ParamType, ArgIdx++))
|
||||
return Result;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
QualType ParamPattern = ParamExpansion->getPattern();
|
||||
PackDeductionScope PackScope(*this, TemplateParams, Deduced, Info,
|
||||
ParamPattern);
|
||||
|
||||
// C++0x [temp.deduct.call]p1:
|
||||
// For a function parameter pack that occurs at the end of the
|
||||
// parameter-declaration-list, the type A of each remaining argument of
|
||||
// the call is compared with the type P of the declarator-id of the
|
||||
// function parameter pack. Each comparison deduces template arguments
|
||||
// for subsequent positions in the template parameter packs expanded by
|
||||
// the function parameter pack. For a function parameter pack that does
|
||||
// not occur at the end of the parameter-declaration-list, the type of
|
||||
// the parameter pack is a non-deduced context.
|
||||
// FIXME: This does not say that subsequent parameters are also non-deduced.
|
||||
// See also DR1388 / DR1399, which effectively says we should keep deducing
|
||||
// after the pack.
|
||||
if (ParamIdx + 1 < NumParamTypes)
|
||||
break;
|
||||
|
||||
QualType ParamPattern = ParamExpansion->getPattern();
|
||||
PackDeductionScope PackScope(*this, TemplateParams, Deduced, Info,
|
||||
ParamPattern);
|
||||
|
||||
for (; ArgIdx < Args.size(); PackScope.nextPackElement(), ++ArgIdx)
|
||||
if (auto Result = DeduceCallArgument(ParamPattern, ArgIdx))
|
||||
return Result;
|
||||
// the function parameter pack. When a function parameter pack appears
|
||||
// in a non-deduced context [not at the end of the list], the type of
|
||||
// that parameter pack is never deduced.
|
||||
//
|
||||
// FIXME: The above rule allows the size of the parameter pack to change
|
||||
// after we skip it (in the non-deduced case). That makes no sense, so
|
||||
// we instead notionally deduce the pack against N arguments, where N is
|
||||
// the length of the explicitly-specified pack if it's expanded by the
|
||||
// parameter pack and 0 otherwise, and we treat each deduction as a
|
||||
// non-deduced context.
|
||||
if (ParamIdx + 1 == NumParamTypes) {
|
||||
for (; ArgIdx < Args.size(); PackScope.nextPackElement(), ++ArgIdx) {
|
||||
ParamTypesForArgChecking.push_back(ParamPattern);
|
||||
if (auto Result = DeduceCallArgument(ParamPattern, ArgIdx))
|
||||
return Result;
|
||||
}
|
||||
} else {
|
||||
// If the parameter type contains an explicitly-specified pack that we
|
||||
// could not expand, skip the number of parameters notionally created
|
||||
// by the expansion.
|
||||
Optional<unsigned> NumExpansions = ParamExpansion->getNumExpansions();
|
||||
if (NumExpansions && !PackScope.isPartiallyExpanded()) {
|
||||
for (unsigned I = 0; I != *NumExpansions && ArgIdx < Args.size();
|
||||
++I, ++ArgIdx) {
|
||||
ParamTypesForArgChecking.push_back(ParamPattern);
|
||||
// FIXME: Should we add OriginalCallArgs for these? What if the
|
||||
// corresponding argument is a list?
|
||||
PackScope.nextPackElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build argument packs for each of the parameter packs expanded by this
|
||||
// pack expansion.
|
||||
if (auto Result = PackScope.finish())
|
||||
return Result;
|
||||
|
||||
// After we've matching against a parameter pack, we're done.
|
||||
break;
|
||||
}
|
||||
|
||||
return FinishTemplateArgumentDeduction(FunctionTemplate, Deduced,
|
||||
NumExplicitlySpecified, Specialization,
|
||||
Info, &OriginalCallArgs,
|
||||
PartialOverloading);
|
||||
return FinishTemplateArgumentDeduction(
|
||||
FunctionTemplate, Deduced, NumExplicitlySpecified, Specialization, Info,
|
||||
&OriginalCallArgs, PartialOverloading,
|
||||
[&]() { return CheckNonDependent(ParamTypesForArgChecking); });
|
||||
}
|
||||
|
||||
QualType Sema::adjustCCAndNoReturn(QualType ArgFunctionType,
|
||||
@ -4230,12 +4282,6 @@ bool Sema::DeduceReturnType(FunctionDecl *FD, SourceLocation Loc,
|
||||
return StillUndeduced;
|
||||
}
|
||||
|
||||
static void
|
||||
MarkUsedTemplateParameters(ASTContext &Ctx, QualType T,
|
||||
bool OnlyDeduced,
|
||||
unsigned Level,
|
||||
llvm::SmallBitVector &Deduced);
|
||||
|
||||
/// \brief If this is a non-static member function,
|
||||
static void
|
||||
AddImplicitObjectParameterType(ASTContext &Context,
|
||||
|
@ -2280,16 +2280,18 @@ namespace {
|
||||
};
|
||||
}
|
||||
|
||||
bool Sema::InstantiateClassTemplateSpecialization(
|
||||
SourceLocation PointOfInstantiation,
|
||||
/// Get the instantiation pattern to use to instantiate the definition of a
|
||||
/// given ClassTemplateSpecializationDecl (either the pattern of the primary
|
||||
/// template or of a partial specialization).
|
||||
static CXXRecordDecl *
|
||||
getPatternForClassTemplateSpecialization(
|
||||
Sema &S, SourceLocation PointOfInstantiation,
|
||||
ClassTemplateSpecializationDecl *ClassTemplateSpec,
|
||||
TemplateSpecializationKind TSK, bool Complain) {
|
||||
// Perform the actual instantiation on the canonical declaration.
|
||||
ClassTemplateSpec = cast<ClassTemplateSpecializationDecl>(
|
||||
ClassTemplateSpec->getCanonicalDecl());
|
||||
if (ClassTemplateSpec->isInvalidDecl())
|
||||
return true;
|
||||
|
||||
Sema::InstantiatingTemplate Inst(S, PointOfInstantiation, ClassTemplateSpec);
|
||||
if (Inst.isInvalid() || Inst.isAlreadyInstantiating())
|
||||
return nullptr;
|
||||
|
||||
ClassTemplateDecl *Template = ClassTemplateSpec->getSpecializedTemplate();
|
||||
CXXRecordDecl *Pattern = nullptr;
|
||||
|
||||
@ -2309,15 +2311,13 @@ bool Sema::InstantiateClassTemplateSpecialization(
|
||||
for (unsigned I = 0, N = PartialSpecs.size(); I != N; ++I) {
|
||||
ClassTemplatePartialSpecializationDecl *Partial = PartialSpecs[I];
|
||||
TemplateDeductionInfo Info(FailedCandidates.getLocation());
|
||||
if (TemplateDeductionResult Result
|
||||
= DeduceTemplateArguments(Partial,
|
||||
ClassTemplateSpec->getTemplateArgs(),
|
||||
Info)) {
|
||||
if (Sema::TemplateDeductionResult Result = S.DeduceTemplateArguments(
|
||||
Partial, ClassTemplateSpec->getTemplateArgs(), Info)) {
|
||||
// Store the failed-deduction information for use in diagnostics, later.
|
||||
// TODO: Actually use the failed-deduction info?
|
||||
FailedCandidates.addCandidate().set(
|
||||
DeclAccessPair::make(Template, AS_public), Partial,
|
||||
MakeDeductionFailureInfo(Context, Result, Info));
|
||||
MakeDeductionFailureInfo(S.Context, Result, Info));
|
||||
(void)Result;
|
||||
} else {
|
||||
Matched.push_back(PartialSpecMatchResult());
|
||||
@ -2347,9 +2347,8 @@ bool Sema::InstantiateClassTemplateSpecialization(
|
||||
for (SmallVectorImpl<MatchResult>::iterator P = Best + 1,
|
||||
PEnd = Matched.end();
|
||||
P != PEnd; ++P) {
|
||||
if (getMoreSpecializedPartialSpecialization(P->Partial, Best->Partial,
|
||||
PointOfInstantiation)
|
||||
== P->Partial)
|
||||
if (S.getMoreSpecializedPartialSpecialization(
|
||||
P->Partial, Best->Partial, PointOfInstantiation) == P->Partial)
|
||||
Best = P;
|
||||
}
|
||||
|
||||
@ -2360,9 +2359,9 @@ bool Sema::InstantiateClassTemplateSpecialization(
|
||||
PEnd = Matched.end();
|
||||
P != PEnd; ++P) {
|
||||
if (P != Best &&
|
||||
getMoreSpecializedPartialSpecialization(P->Partial, Best->Partial,
|
||||
PointOfInstantiation)
|
||||
!= Best->Partial) {
|
||||
S.getMoreSpecializedPartialSpecialization(P->Partial, Best->Partial,
|
||||
PointOfInstantiation) !=
|
||||
Best->Partial) {
|
||||
Ambiguous = true;
|
||||
break;
|
||||
}
|
||||
@ -2370,20 +2369,20 @@ bool Sema::InstantiateClassTemplateSpecialization(
|
||||
|
||||
if (Ambiguous) {
|
||||
// Partial ordering did not produce a clear winner. Complain.
|
||||
Inst.Clear();
|
||||
ClassTemplateSpec->setInvalidDecl();
|
||||
Diag(PointOfInstantiation, diag::err_partial_spec_ordering_ambiguous)
|
||||
S.Diag(PointOfInstantiation, diag::err_partial_spec_ordering_ambiguous)
|
||||
<< ClassTemplateSpec;
|
||||
|
||||
// Print the matching partial specializations.
|
||||
for (SmallVectorImpl<MatchResult>::iterator P = Matched.begin(),
|
||||
PEnd = Matched.end();
|
||||
P != PEnd; ++P)
|
||||
Diag(P->Partial->getLocation(), diag::note_partial_spec_match)
|
||||
<< getTemplateArgumentBindingsText(
|
||||
P->Partial->getTemplateParameters(),
|
||||
*P->Args);
|
||||
S.Diag(P->Partial->getLocation(), diag::note_partial_spec_match)
|
||||
<< S.getTemplateArgumentBindingsText(
|
||||
P->Partial->getTemplateParameters(), *P->Args);
|
||||
|
||||
return true;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2416,13 +2415,27 @@ bool Sema::InstantiateClassTemplateSpecialization(
|
||||
Pattern = OrigTemplate->getTemplatedDecl();
|
||||
}
|
||||
|
||||
bool Result = InstantiateClass(PointOfInstantiation, ClassTemplateSpec,
|
||||
Pattern,
|
||||
getTemplateInstantiationArgs(ClassTemplateSpec),
|
||||
TSK,
|
||||
Complain);
|
||||
return Pattern;
|
||||
}
|
||||
|
||||
return Result;
|
||||
bool Sema::InstantiateClassTemplateSpecialization(
|
||||
SourceLocation PointOfInstantiation,
|
||||
ClassTemplateSpecializationDecl *ClassTemplateSpec,
|
||||
TemplateSpecializationKind TSK, bool Complain) {
|
||||
// Perform the actual instantiation on the canonical declaration.
|
||||
ClassTemplateSpec = cast<ClassTemplateSpecializationDecl>(
|
||||
ClassTemplateSpec->getCanonicalDecl());
|
||||
if (ClassTemplateSpec->isInvalidDecl())
|
||||
return true;
|
||||
|
||||
CXXRecordDecl *Pattern = getPatternForClassTemplateSpecialization(
|
||||
*this, PointOfInstantiation, ClassTemplateSpec, TSK, Complain);
|
||||
if (!Pattern)
|
||||
return true;
|
||||
|
||||
return InstantiateClass(PointOfInstantiation, ClassTemplateSpec, Pattern,
|
||||
getTemplateInstantiationArgs(ClassTemplateSpec), TSK,
|
||||
Complain);
|
||||
}
|
||||
|
||||
/// \brief Instantiates the definitions of all of the member
|
||||
|
@ -168,39 +168,59 @@ static void instantiateDependentAlignValueAttr(
|
||||
Aligned->getSpellingListIndex());
|
||||
}
|
||||
|
||||
static void instantiateDependentEnableIfAttr(
|
||||
static Expr *instantiateDependentFunctionAttrCondition(
|
||||
Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
|
||||
const EnableIfAttr *A, const Decl *Tmpl, Decl *New) {
|
||||
const Attr *A, Expr *OldCond, const Decl *Tmpl, FunctionDecl *New) {
|
||||
Expr *Cond = nullptr;
|
||||
{
|
||||
EnterExpressionEvaluationContext Unevaluated(S, Sema::Unevaluated);
|
||||
ExprResult Result = S.SubstExpr(A->getCond(), TemplateArgs);
|
||||
Sema::ContextRAII SwitchContext(S, New);
|
||||
EnterExpressionEvaluationContext Unevaluated(S, Sema::ConstantEvaluated);
|
||||
ExprResult Result = S.SubstExpr(OldCond, TemplateArgs);
|
||||
if (Result.isInvalid())
|
||||
return;
|
||||
return nullptr;
|
||||
Cond = Result.getAs<Expr>();
|
||||
}
|
||||
if (!Cond->isTypeDependent()) {
|
||||
ExprResult Converted = S.PerformContextuallyConvertToBool(Cond);
|
||||
if (Converted.isInvalid())
|
||||
return;
|
||||
return nullptr;
|
||||
Cond = Converted.get();
|
||||
}
|
||||
|
||||
SmallVector<PartialDiagnosticAt, 8> Diags;
|
||||
if (A->getCond()->isValueDependent() && !Cond->isValueDependent() &&
|
||||
!Expr::isPotentialConstantExprUnevaluated(Cond, cast<FunctionDecl>(New),
|
||||
Diags)) {
|
||||
S.Diag(A->getLocation(), diag::err_enable_if_never_constant_expr);
|
||||
for (int I = 0, N = Diags.size(); I != N; ++I)
|
||||
S.Diag(Diags[I].first, Diags[I].second);
|
||||
return;
|
||||
if (OldCond->isValueDependent() && !Cond->isValueDependent() &&
|
||||
!Expr::isPotentialConstantExprUnevaluated(Cond, New, Diags)) {
|
||||
S.Diag(A->getLocation(), diag::err_attr_cond_never_constant_expr) << A;
|
||||
for (const auto &P : Diags)
|
||||
S.Diag(P.first, P.second);
|
||||
return nullptr;
|
||||
}
|
||||
return Cond;
|
||||
}
|
||||
|
||||
EnableIfAttr *EIA = new (S.getASTContext())
|
||||
EnableIfAttr(A->getLocation(), S.getASTContext(), Cond,
|
||||
A->getMessage(),
|
||||
A->getSpellingListIndex());
|
||||
New->addAttr(EIA);
|
||||
static void instantiateDependentEnableIfAttr(
|
||||
Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
|
||||
const EnableIfAttr *EIA, const Decl *Tmpl, FunctionDecl *New) {
|
||||
Expr *Cond = instantiateDependentFunctionAttrCondition(
|
||||
S, TemplateArgs, EIA, EIA->getCond(), Tmpl, New);
|
||||
|
||||
if (Cond)
|
||||
New->addAttr(new (S.getASTContext()) EnableIfAttr(
|
||||
EIA->getLocation(), S.getASTContext(), Cond, EIA->getMessage(),
|
||||
EIA->getSpellingListIndex()));
|
||||
}
|
||||
|
||||
static void instantiateDependentDiagnoseIfAttr(
|
||||
Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
|
||||
const DiagnoseIfAttr *DIA, const Decl *Tmpl, FunctionDecl *New) {
|
||||
Expr *Cond = instantiateDependentFunctionAttrCondition(
|
||||
S, TemplateArgs, DIA, DIA->getCond(), Tmpl, New);
|
||||
|
||||
if (Cond)
|
||||
New->addAttr(new (S.getASTContext()) DiagnoseIfAttr(
|
||||
DIA->getLocation(), S.getASTContext(), Cond, DIA->getMessage(),
|
||||
DIA->getDiagnosticType(), DIA->getArgDependent(), New,
|
||||
DIA->getSpellingListIndex()));
|
||||
}
|
||||
|
||||
// Constructs and adds to New a new instance of CUDALaunchBoundsAttr using
|
||||
@ -334,7 +354,13 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
|
||||
|
||||
if (const auto *EnableIf = dyn_cast<EnableIfAttr>(TmplAttr)) {
|
||||
instantiateDependentEnableIfAttr(*this, TemplateArgs, EnableIf, Tmpl,
|
||||
New);
|
||||
cast<FunctionDecl>(New));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (const auto *DiagnoseIf = dyn_cast<DiagnoseIfAttr>(TmplAttr)) {
|
||||
instantiateDependentDiagnoseIfAttr(*this, TemplateArgs, DiagnoseIf, Tmpl,
|
||||
cast<FunctionDecl>(New));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -4654,17 +4654,6 @@ uint64_t ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot,
|
||||
// If we're emitting a module, write out the submodule information.
|
||||
if (WritingModule)
|
||||
WriteSubmodules(WritingModule);
|
||||
else if (!getLangOpts().CurrentModule.empty()) {
|
||||
// If we're building a PCH in the implementation of a module, we may need
|
||||
// the description of the current module.
|
||||
//
|
||||
// FIXME: We may need other modules that we did not load from an AST file,
|
||||
// such as if a module declares a 'conflicts' on a different module.
|
||||
Module *M = PP.getHeaderSearchInfo().getModuleMap().findModule(
|
||||
getLangOpts().CurrentModule);
|
||||
if (M && !M->IsFromModuleFile)
|
||||
WriteSubmodules(M);
|
||||
}
|
||||
|
||||
Stream.EmitRecord(SPECIAL_TYPES, SpecialTypes);
|
||||
|
||||
|
@ -39,6 +39,7 @@ add_clang_library(clangStaticAnalyzerCheckers
|
||||
GenericTaintChecker.cpp
|
||||
GTestChecker.cpp
|
||||
IdenticalExprChecker.cpp
|
||||
IteratorPastEndChecker.cpp
|
||||
IvarInvalidationChecker.cpp
|
||||
LLVMConventionsChecker.cpp
|
||||
LocalizationChecker.cpp
|
||||
|
842
lib/StaticAnalyzer/Checkers/IteratorPastEndChecker.cpp
Normal file
842
lib/StaticAnalyzer/Checkers/IteratorPastEndChecker.cpp
Normal file
@ -0,0 +1,842 @@
|
||||
//===-- IteratorPastEndChecker.cpp --------------------------------*- C++ -*--//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Defines a checker for using iterators outside their range (past end). Usage
|
||||
// means here dereferencing, incrementing etc.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ClangSACheckers.h"
|
||||
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
|
||||
#include "clang/StaticAnalyzer/Core/Checker.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
using namespace clang;
|
||||
using namespace ento;
|
||||
|
||||
namespace {
|
||||
struct IteratorPosition {
|
||||
private:
|
||||
enum Kind { InRange, OutofRange } K;
|
||||
IteratorPosition(Kind InK) : K(InK) {}
|
||||
|
||||
public:
|
||||
bool isInRange() const { return K == InRange; }
|
||||
bool isOutofRange() const { return K == OutofRange; }
|
||||
|
||||
static IteratorPosition getInRange() { return IteratorPosition(InRange); }
|
||||
static IteratorPosition getOutofRange() {
|
||||
return IteratorPosition(OutofRange);
|
||||
}
|
||||
|
||||
bool operator==(const IteratorPosition &X) const { return K == X.K; }
|
||||
bool operator!=(const IteratorPosition &X) const { return K != X.K; }
|
||||
void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); }
|
||||
};
|
||||
|
||||
typedef llvm::PointerUnion<const MemRegion *, SymbolRef> RegionOrSymbol;
|
||||
|
||||
struct IteratorComparison {
|
||||
private:
|
||||
RegionOrSymbol Left, Right;
|
||||
bool Equality;
|
||||
|
||||
public:
|
||||
IteratorComparison(RegionOrSymbol L, RegionOrSymbol R, bool Eq)
|
||||
: Left(L), Right(R), Equality(Eq) {}
|
||||
|
||||
RegionOrSymbol getLeft() const { return Left; }
|
||||
RegionOrSymbol getRight() const { return Right; }
|
||||
bool isEquality() const { return Equality; }
|
||||
bool operator==(const IteratorComparison &X) const {
|
||||
return Left == X.Left && Right == X.Right && Equality == X.Equality;
|
||||
}
|
||||
bool operator!=(const IteratorComparison &X) const {
|
||||
return Left != X.Left || Right != X.Right || Equality != X.Equality;
|
||||
}
|
||||
void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Equality); }
|
||||
};
|
||||
|
||||
class IteratorPastEndChecker
|
||||
: public Checker<
|
||||
check::PreCall, check::PostCall, check::PreStmt<CXXOperatorCallExpr>,
|
||||
check::PostStmt<CXXConstructExpr>, check::PostStmt<DeclStmt>,
|
||||
check::PostStmt<MaterializeTemporaryExpr>, check::BeginFunction,
|
||||
check::DeadSymbols, eval::Assume, eval::Call> {
|
||||
mutable IdentifierInfo *II_find = nullptr,
|
||||
*II_find_end = nullptr, *II_find_first_of = nullptr,
|
||||
*II_find_if = nullptr, *II_find_if_not = nullptr,
|
||||
*II_lower_bound = nullptr, *II_upper_bound = nullptr,
|
||||
*II_search = nullptr, *II_search_n = nullptr;
|
||||
|
||||
std::unique_ptr<BugType> PastEndBugType;
|
||||
|
||||
void handleComparison(CheckerContext &C, const SVal &RetVal, const SVal &LVal,
|
||||
const SVal &RVal, OverloadedOperatorKind Op) const;
|
||||
void handleAccess(CheckerContext &C, const SVal &Val) const;
|
||||
void handleDecrement(CheckerContext &C, const SVal &Val) const;
|
||||
void handleEnd(CheckerContext &C, const SVal &RetVal) const;
|
||||
|
||||
bool evalFind(CheckerContext &C, const CallExpr *CE) const;
|
||||
bool evalFindEnd(CheckerContext &C, const CallExpr *CE) const;
|
||||
bool evalFindFirstOf(CheckerContext &C, const CallExpr *CE) const;
|
||||
bool evalFindIf(CheckerContext &C, const CallExpr *CE) const;
|
||||
bool evalFindIfNot(CheckerContext &C, const CallExpr *CE) const;
|
||||
bool evalLowerBound(CheckerContext &C, const CallExpr *CE) const;
|
||||
bool evalUpperBound(CheckerContext &C, const CallExpr *CE) const;
|
||||
bool evalSearch(CheckerContext &C, const CallExpr *CE) const;
|
||||
bool evalSearchN(CheckerContext &C, const CallExpr *CE) const;
|
||||
void Find(CheckerContext &C, const CallExpr *CE) const;
|
||||
|
||||
void reportPastEndBug(const StringRef &Message, const SVal &Val,
|
||||
CheckerContext &C, ExplodedNode *ErrNode) const;
|
||||
void initIdentifiers(ASTContext &Ctx) const;
|
||||
|
||||
public:
|
||||
IteratorPastEndChecker();
|
||||
|
||||
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
|
||||
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
|
||||
void checkPreStmt(const CXXOperatorCallExpr *COCE, CheckerContext &C) const;
|
||||
void checkBeginFunction(CheckerContext &C) const;
|
||||
void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const;
|
||||
void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const;
|
||||
void checkPostStmt(const MaterializeTemporaryExpr *MTE,
|
||||
CheckerContext &C) const;
|
||||
void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
|
||||
ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
|
||||
bool Assumption) const;
|
||||
bool evalCall(const CallExpr *CE, CheckerContext &C) const;
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, IteratorPosition)
|
||||
REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *,
|
||||
IteratorPosition)
|
||||
|
||||
REGISTER_MAP_WITH_PROGRAMSTATE(IteratorComparisonMap, const SymExpr *,
|
||||
IteratorComparison)
|
||||
|
||||
#define INIT_ID(Id) \
|
||||
if (!II_##Id) \
|
||||
II_##Id = &Ctx.Idents.get(#Id)
|
||||
|
||||
namespace {
|
||||
|
||||
bool isIteratorType(const QualType &Type);
|
||||
bool isIterator(const CXXRecordDecl *CRD);
|
||||
bool isEndCall(const FunctionDecl *Func);
|
||||
bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
|
||||
bool isAccessOperator(OverloadedOperatorKind OK);
|
||||
bool isDecrementOperator(OverloadedOperatorKind OK);
|
||||
BinaryOperator::Opcode getOpcode(const SymExpr *SE);
|
||||
const RegionOrSymbol getRegionOrSymbol(const SVal &Val);
|
||||
const ProgramStateRef processComparison(ProgramStateRef State,
|
||||
RegionOrSymbol LVal,
|
||||
RegionOrSymbol RVal, bool Equal);
|
||||
const ProgramStateRef saveComparison(ProgramStateRef State,
|
||||
const SymExpr *Condition, const SVal &LVal,
|
||||
const SVal &RVal, bool Eq);
|
||||
const IteratorComparison *loadComparison(ProgramStateRef State,
|
||||
const SymExpr *Condition);
|
||||
const IteratorPosition *getIteratorPosition(ProgramStateRef State,
|
||||
const SVal &Val);
|
||||
const IteratorPosition *getIteratorPosition(ProgramStateRef State,
|
||||
RegionOrSymbol RegOrSym);
|
||||
ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
|
||||
IteratorPosition Pos);
|
||||
ProgramStateRef setIteratorPosition(ProgramStateRef State,
|
||||
RegionOrSymbol RegOrSym,
|
||||
IteratorPosition Pos);
|
||||
ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
|
||||
RegionOrSymbol RegOrSym,
|
||||
IteratorPosition Pos, bool Equal);
|
||||
bool contradictingIteratorPositions(IteratorPosition Pos1,
|
||||
IteratorPosition Pos2, bool Equal);
|
||||
}
|
||||
|
||||
IteratorPastEndChecker::IteratorPastEndChecker() {
|
||||
PastEndBugType.reset(
|
||||
new BugType(this, "Iterator Past End", "Misuse of STL APIs"));
|
||||
PastEndBugType->setSuppressOnSink(true);
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::checkPreCall(const CallEvent &Call,
|
||||
CheckerContext &C) const {
|
||||
// Check for access past end
|
||||
const auto *Func = Call.getDecl()->getAsFunction();
|
||||
if (!Func)
|
||||
return;
|
||||
if (Func->isOverloadedOperator()) {
|
||||
if (isAccessOperator(Func->getOverloadedOperator())) {
|
||||
if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
|
||||
handleAccess(C, InstCall->getCXXThisVal());
|
||||
} else {
|
||||
handleAccess(C, Call.getArgSVal(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::checkPostCall(const CallEvent &Call,
|
||||
CheckerContext &C) const {
|
||||
// Record end() iterators, iterator decrementation and comparison
|
||||
const auto *Func = Call.getDecl()->getAsFunction();
|
||||
if (!Func)
|
||||
return;
|
||||
if (Func->isOverloadedOperator()) {
|
||||
const auto Op = Func->getOverloadedOperator();
|
||||
if (isSimpleComparisonOperator(Op)) {
|
||||
if (Func->isCXXInstanceMember()) {
|
||||
const auto &InstCall = static_cast<const CXXInstanceCall &>(Call);
|
||||
handleComparison(C, InstCall.getReturnValue(), InstCall.getCXXThisVal(),
|
||||
InstCall.getArgSVal(0), Op);
|
||||
} else {
|
||||
handleComparison(C, Call.getReturnValue(), Call.getArgSVal(0),
|
||||
Call.getArgSVal(1), Op);
|
||||
}
|
||||
} else if (isDecrementOperator(Func->getOverloadedOperator())) {
|
||||
if (Func->isCXXInstanceMember()) {
|
||||
const auto &InstCall = static_cast<const CXXInstanceCall &>(Call);
|
||||
handleDecrement(C, InstCall.getCXXThisVal());
|
||||
} else {
|
||||
handleDecrement(C, Call.getArgSVal(0));
|
||||
}
|
||||
}
|
||||
} else if (Func->isCXXInstanceMember()) {
|
||||
if (!isEndCall(Func))
|
||||
return;
|
||||
if (!isIteratorType(Call.getResultType()))
|
||||
return;
|
||||
handleEnd(C, Call.getReturnValue());
|
||||
}
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::checkPreStmt(const CXXOperatorCallExpr *COCE,
|
||||
CheckerContext &C) const {
|
||||
const auto *ThisExpr = COCE->getArg(0);
|
||||
|
||||
auto State = C.getState();
|
||||
const auto *LCtx = C.getPredecessor()->getLocationContext();
|
||||
|
||||
const auto CurrentThis = State->getSVal(ThisExpr, LCtx);
|
||||
if (const auto *Reg = CurrentThis.getAsRegion()) {
|
||||
if (!Reg->getAs<CXXTempObjectRegion>())
|
||||
return;
|
||||
const auto OldState = C.getPredecessor()->getFirstPred()->getState();
|
||||
const auto OldThis = OldState->getSVal(ThisExpr, LCtx);
|
||||
const auto *Pos = getIteratorPosition(OldState, OldThis);
|
||||
if (!Pos)
|
||||
return;
|
||||
State = setIteratorPosition(State, CurrentThis, *Pos);
|
||||
C.addTransition(State);
|
||||
}
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::checkBeginFunction(CheckerContext &C) const {
|
||||
// Copy state of iterator arguments to iterator parameters
|
||||
auto State = C.getState();
|
||||
const auto *LCtx = C.getLocationContext();
|
||||
|
||||
const auto *Site = cast<StackFrameContext>(LCtx)->getCallSite();
|
||||
if (!Site)
|
||||
return;
|
||||
|
||||
const auto *FD = dyn_cast<FunctionDecl>(LCtx->getDecl());
|
||||
if (!FD)
|
||||
return;
|
||||
|
||||
const auto *CE = dyn_cast<CallExpr>(Site);
|
||||
if (!CE)
|
||||
return;
|
||||
|
||||
bool Change = false;
|
||||
int idx = 0;
|
||||
for (const auto P : FD->parameters()) {
|
||||
auto Param = State->getLValue(P, LCtx);
|
||||
auto Arg = State->getSVal(CE->getArg(idx++), LCtx->getParent());
|
||||
const auto *Pos = getIteratorPosition(State, Arg);
|
||||
if (!Pos)
|
||||
continue;
|
||||
State = setIteratorPosition(State, Param, *Pos);
|
||||
Change = true;
|
||||
}
|
||||
if (Change) {
|
||||
C.addTransition(State);
|
||||
}
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::checkPostStmt(const CXXConstructExpr *CCE,
|
||||
CheckerContext &C) const {
|
||||
// Transfer iterator state in case of copy or move by constructor
|
||||
const auto *ctr = CCE->getConstructor();
|
||||
if (!ctr->isCopyOrMoveConstructor())
|
||||
return;
|
||||
const auto *RHSExpr = CCE->getArg(0);
|
||||
|
||||
auto State = C.getState();
|
||||
const auto *LCtx = C.getLocationContext();
|
||||
|
||||
const auto RetVal = State->getSVal(CCE, LCtx);
|
||||
|
||||
const auto RHSVal = State->getSVal(RHSExpr, LCtx);
|
||||
const auto *RHSPos = getIteratorPosition(State, RHSVal);
|
||||
if (!RHSPos)
|
||||
return;
|
||||
State = setIteratorPosition(State, RetVal, *RHSPos);
|
||||
C.addTransition(State);
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::checkPostStmt(const DeclStmt *DS,
|
||||
CheckerContext &C) const {
|
||||
// Transfer iterator state to new variable declaration
|
||||
for (const auto *D : DS->decls()) {
|
||||
const auto *VD = dyn_cast<VarDecl>(D);
|
||||
if (!VD || !VD->hasInit())
|
||||
continue;
|
||||
|
||||
auto State = C.getState();
|
||||
const auto *LCtx = C.getPredecessor()->getLocationContext();
|
||||
const auto *Pos =
|
||||
getIteratorPosition(State, State->getSVal(VD->getInit(), LCtx));
|
||||
if (!Pos)
|
||||
continue;
|
||||
State = setIteratorPosition(State, State->getLValue(VD, LCtx), *Pos);
|
||||
C.addTransition(State);
|
||||
}
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE,
|
||||
CheckerContext &C) const {
|
||||
/* Transfer iterator state for to temporary objects */
|
||||
auto State = C.getState();
|
||||
const auto *LCtx = C.getPredecessor()->getLocationContext();
|
||||
const auto *Pos =
|
||||
getIteratorPosition(State, State->getSVal(MTE->GetTemporaryExpr(), LCtx));
|
||||
if (!Pos)
|
||||
return;
|
||||
State = setIteratorPosition(State, State->getSVal(MTE, LCtx), *Pos);
|
||||
C.addTransition(State);
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::checkDeadSymbols(SymbolReaper &SR,
|
||||
CheckerContext &C) const {
|
||||
auto State = C.getState();
|
||||
|
||||
auto RegionMap = State->get<IteratorRegionMap>();
|
||||
for (const auto Reg : RegionMap) {
|
||||
if (!SR.isLiveRegion(Reg.first)) {
|
||||
State = State->remove<IteratorRegionMap>(Reg.first);
|
||||
}
|
||||
}
|
||||
|
||||
auto SymbolMap = State->get<IteratorSymbolMap>();
|
||||
for (const auto Sym : SymbolMap) {
|
||||
if (SR.isDead(Sym.first)) {
|
||||
State = State->remove<IteratorSymbolMap>(Sym.first);
|
||||
}
|
||||
}
|
||||
|
||||
auto ComparisonMap = State->get<IteratorComparisonMap>();
|
||||
for (const auto Comp : ComparisonMap) {
|
||||
if (SR.isDead(Comp.first)) {
|
||||
State = State->remove<IteratorComparisonMap>(Comp.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ProgramStateRef IteratorPastEndChecker::evalAssume(ProgramStateRef State,
|
||||
SVal Cond,
|
||||
bool Assumption) const {
|
||||
// Load recorded comparison and transfer iterator state between sides
|
||||
// according to comparison operator and assumption
|
||||
const auto *SE = Cond.getAsSymExpr();
|
||||
if (!SE)
|
||||
return State;
|
||||
|
||||
auto Opc = getOpcode(SE);
|
||||
if (Opc != BO_EQ && Opc != BO_NE)
|
||||
return State;
|
||||
|
||||
bool Negated = false;
|
||||
const auto *Comp = loadComparison(State, SE);
|
||||
if (!Comp) {
|
||||
// Try negated comparison, which is a SymExpr to 0 integer comparison
|
||||
const auto *SIE = dyn_cast<SymIntExpr>(SE);
|
||||
if (!SIE)
|
||||
return State;
|
||||
|
||||
if (SIE->getRHS() != 0)
|
||||
return State;
|
||||
|
||||
SE = SIE->getLHS();
|
||||
Negated = SIE->getOpcode() == BO_EQ; // Equal to zero means negation
|
||||
Opc = getOpcode(SE);
|
||||
if (Opc != BO_EQ && Opc != BO_NE)
|
||||
return State;
|
||||
|
||||
Comp = loadComparison(State, SE);
|
||||
if (!Comp)
|
||||
return State;
|
||||
}
|
||||
|
||||
return processComparison(State, Comp->getLeft(), Comp->getRight(),
|
||||
(Comp->isEquality() == Assumption) != Negated);
|
||||
}
|
||||
|
||||
// FIXME: Evaluation of these STL calls should be moved to StdCLibraryFunctions
|
||||
// checker (see patch r284960) or another similar checker for C++ STL
|
||||
// functions (e.g. StdCXXLibraryFunctions or StdCppLibraryFunctions).
|
||||
bool IteratorPastEndChecker::evalCall(const CallExpr *CE,
|
||||
CheckerContext &C) const {
|
||||
const FunctionDecl *FD = C.getCalleeDecl(CE);
|
||||
if (!FD)
|
||||
return false;
|
||||
|
||||
ASTContext &Ctx = C.getASTContext();
|
||||
initIdentifiers(Ctx);
|
||||
|
||||
if (FD->getKind() == Decl::Function) {
|
||||
if (FD->isInStdNamespace()) {
|
||||
if (FD->getIdentifier() == II_find) {
|
||||
return evalFind(C, CE);
|
||||
} else if (FD->getIdentifier() == II_find_end) {
|
||||
return evalFindEnd(C, CE);
|
||||
} else if (FD->getIdentifier() == II_find_first_of) {
|
||||
return evalFindFirstOf(C, CE);
|
||||
} else if (FD->getIdentifier() == II_find_if) {
|
||||
return evalFindIf(C, CE);
|
||||
} else if (FD->getIdentifier() == II_find_if) {
|
||||
return evalFindIf(C, CE);
|
||||
} else if (FD->getIdentifier() == II_find_if_not) {
|
||||
return evalFindIfNot(C, CE);
|
||||
} else if (FD->getIdentifier() == II_upper_bound) {
|
||||
return evalUpperBound(C, CE);
|
||||
} else if (FD->getIdentifier() == II_lower_bound) {
|
||||
return evalLowerBound(C, CE);
|
||||
} else if (FD->getIdentifier() == II_search) {
|
||||
return evalSearch(C, CE);
|
||||
} else if (FD->getIdentifier() == II_search_n) {
|
||||
return evalSearchN(C, CE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::handleComparison(CheckerContext &C,
|
||||
const SVal &RetVal,
|
||||
const SVal &LVal,
|
||||
const SVal &RVal,
|
||||
OverloadedOperatorKind Op) const {
|
||||
// Record the operands and the operator of the comparison for the next
|
||||
// evalAssume, if the result is a symbolic expression. If it is a concrete
|
||||
// value (only one branch is possible), then transfer the state between
|
||||
// the operands according to the operator and the result
|
||||
auto State = C.getState();
|
||||
if (const auto *Condition = RetVal.getAsSymbolicExpression()) {
|
||||
const auto *LPos = getIteratorPosition(State, LVal);
|
||||
const auto *RPos = getIteratorPosition(State, RVal);
|
||||
if (!LPos && !RPos)
|
||||
return;
|
||||
State = saveComparison(State, Condition, LVal, RVal, Op == OO_EqualEqual);
|
||||
C.addTransition(State);
|
||||
} else if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
|
||||
if ((State = processComparison(
|
||||
State, getRegionOrSymbol(LVal), getRegionOrSymbol(RVal),
|
||||
(Op == OO_EqualEqual) == (TruthVal->getValue() != 0)))) {
|
||||
C.addTransition(State);
|
||||
} else {
|
||||
C.generateSink(State, C.getPredecessor());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::handleAccess(CheckerContext &C,
|
||||
const SVal &Val) const {
|
||||
auto State = C.getState();
|
||||
const auto *Pos = getIteratorPosition(State, Val);
|
||||
if (Pos && Pos->isOutofRange()) {
|
||||
auto *N = C.generateNonFatalErrorNode(State);
|
||||
if (!N) {
|
||||
return;
|
||||
}
|
||||
reportPastEndBug("Iterator accessed past its end.", Val, C, N);
|
||||
}
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::handleDecrement(CheckerContext &C,
|
||||
const SVal &Val) const {
|
||||
auto State = C.getState();
|
||||
const auto *Pos = getIteratorPosition(State, Val);
|
||||
if (Pos && Pos->isOutofRange()) {
|
||||
State = setIteratorPosition(State, Val, IteratorPosition::getInRange());
|
||||
// FIXME: We could also check for iterators ahead of their beginnig in the
|
||||
// future, but currently we do not care for such errors. We also
|
||||
// assume that the iterator is not past its end by more then one
|
||||
// position.
|
||||
C.addTransition(State);
|
||||
}
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::handleEnd(CheckerContext &C,
|
||||
const SVal &RetVal) const {
|
||||
auto State = C.getState();
|
||||
State = setIteratorPosition(State, RetVal, IteratorPosition::getOutofRange());
|
||||
C.addTransition(State);
|
||||
}
|
||||
|
||||
bool IteratorPastEndChecker::evalFind(CheckerContext &C,
|
||||
const CallExpr *CE) const {
|
||||
if (CE->getNumArgs() == 3 && isIteratorType(CE->getArg(0)->getType()) &&
|
||||
isIteratorType(CE->getArg(1)->getType())) {
|
||||
Find(C, CE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IteratorPastEndChecker::evalFindEnd(CheckerContext &C,
|
||||
const CallExpr *CE) const {
|
||||
if ((CE->getNumArgs() == 4 || CE->getNumArgs() == 5) &&
|
||||
isIteratorType(CE->getArg(0)->getType()) &&
|
||||
isIteratorType(CE->getArg(1)->getType()) &&
|
||||
isIteratorType(CE->getArg(2)->getType()) &&
|
||||
isIteratorType(CE->getArg(3)->getType())) {
|
||||
Find(C, CE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IteratorPastEndChecker::evalFindFirstOf(CheckerContext &C,
|
||||
const CallExpr *CE) const {
|
||||
if ((CE->getNumArgs() == 4 || CE->getNumArgs() == 5) &&
|
||||
isIteratorType(CE->getArg(0)->getType()) &&
|
||||
isIteratorType(CE->getArg(1)->getType()) &&
|
||||
isIteratorType(CE->getArg(2)->getType()) &&
|
||||
isIteratorType(CE->getArg(3)->getType())) {
|
||||
Find(C, CE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IteratorPastEndChecker::evalFindIf(CheckerContext &C,
|
||||
const CallExpr *CE) const {
|
||||
if (CE->getNumArgs() == 3 && isIteratorType(CE->getArg(0)->getType()) &&
|
||||
isIteratorType(CE->getArg(1)->getType())) {
|
||||
Find(C, CE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IteratorPastEndChecker::evalFindIfNot(CheckerContext &C,
|
||||
const CallExpr *CE) const {
|
||||
if (CE->getNumArgs() == 3 && isIteratorType(CE->getArg(0)->getType()) &&
|
||||
isIteratorType(CE->getArg(1)->getType())) {
|
||||
Find(C, CE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IteratorPastEndChecker::evalLowerBound(CheckerContext &C,
|
||||
const CallExpr *CE) const {
|
||||
if ((CE->getNumArgs() == 3 || CE->getNumArgs() == 4) &&
|
||||
isIteratorType(CE->getArg(0)->getType()) &&
|
||||
isIteratorType(CE->getArg(1)->getType())) {
|
||||
Find(C, CE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IteratorPastEndChecker::evalUpperBound(CheckerContext &C,
|
||||
const CallExpr *CE) const {
|
||||
if ((CE->getNumArgs() == 3 || CE->getNumArgs() == 4) &&
|
||||
isIteratorType(CE->getArg(0)->getType()) &&
|
||||
isIteratorType(CE->getArg(1)->getType())) {
|
||||
Find(C, CE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IteratorPastEndChecker::evalSearch(CheckerContext &C,
|
||||
const CallExpr *CE) const {
|
||||
if ((CE->getNumArgs() == 4 || CE->getNumArgs() == 5) &&
|
||||
isIteratorType(CE->getArg(0)->getType()) &&
|
||||
isIteratorType(CE->getArg(1)->getType()) &&
|
||||
isIteratorType(CE->getArg(2)->getType()) &&
|
||||
isIteratorType(CE->getArg(3)->getType())) {
|
||||
Find(C, CE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IteratorPastEndChecker::evalSearchN(CheckerContext &C,
|
||||
const CallExpr *CE) const {
|
||||
if ((CE->getNumArgs() == 4 || CE->getNumArgs() == 5) &&
|
||||
isIteratorType(CE->getArg(0)->getType()) &&
|
||||
isIteratorType(CE->getArg(1)->getType())) {
|
||||
Find(C, CE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::Find(CheckerContext &C, const CallExpr *CE) const {
|
||||
auto state = C.getState();
|
||||
auto &svalBuilder = C.getSValBuilder();
|
||||
const auto *LCtx = C.getLocationContext();
|
||||
|
||||
auto RetVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
|
||||
auto SecondParam = state->getSVal(CE->getArg(1), LCtx);
|
||||
|
||||
auto stateFound = state->BindExpr(CE, LCtx, RetVal);
|
||||
auto stateNotFound = state->BindExpr(CE, LCtx, SecondParam);
|
||||
|
||||
C.addTransition(stateFound);
|
||||
C.addTransition(stateNotFound);
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::reportPastEndBug(const StringRef &Message,
|
||||
const SVal &Val,
|
||||
CheckerContext &C,
|
||||
ExplodedNode *ErrNode) const {
|
||||
auto R = llvm::make_unique<BugReport>(*PastEndBugType, Message, ErrNode);
|
||||
R->markInteresting(Val);
|
||||
C.emitReport(std::move(R));
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::initIdentifiers(ASTContext &Ctx) const {
|
||||
INIT_ID(find);
|
||||
INIT_ID(find_end);
|
||||
INIT_ID(find_first_of);
|
||||
INIT_ID(find_if);
|
||||
INIT_ID(find_if_not);
|
||||
INIT_ID(lower_bound);
|
||||
INIT_ID(upper_bound);
|
||||
INIT_ID(search);
|
||||
INIT_ID(search_n);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool isIteratorType(const QualType &Type) {
|
||||
if (Type->isPointerType())
|
||||
return true;
|
||||
|
||||
const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
|
||||
return isIterator(CRD);
|
||||
}
|
||||
|
||||
bool isIterator(const CXXRecordDecl *CRD) {
|
||||
if (!CRD)
|
||||
return false;
|
||||
|
||||
const auto Name = CRD->getName();
|
||||
if (!(Name.endswith_lower("iterator") || Name.endswith_lower("iter") ||
|
||||
Name.endswith_lower("it")))
|
||||
return false;
|
||||
|
||||
bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false,
|
||||
HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false;
|
||||
for (const auto *Method : CRD->methods()) {
|
||||
if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) {
|
||||
if (Ctor->isCopyConstructor()) {
|
||||
HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
|
||||
HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public;
|
||||
continue;
|
||||
}
|
||||
if (Method->isCopyAssignmentOperator()) {
|
||||
HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public;
|
||||
continue;
|
||||
}
|
||||
if (!Method->isOverloadedOperator())
|
||||
continue;
|
||||
const auto OPK = Method->getOverloadedOperator();
|
||||
if (OPK == OO_PlusPlus) {
|
||||
HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0);
|
||||
HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1);
|
||||
continue;
|
||||
}
|
||||
if (OPK == OO_Star) {
|
||||
HasDerefOp = (Method->getNumParams() == 0);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp &&
|
||||
HasPostIncrOp && HasDerefOp;
|
||||
}
|
||||
|
||||
bool isEndCall(const FunctionDecl *Func) {
|
||||
const auto *IdInfo = Func->getIdentifier();
|
||||
if (!IdInfo)
|
||||
return false;
|
||||
return IdInfo->getName().endswith_lower("end");
|
||||
}
|
||||
|
||||
bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
|
||||
return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
|
||||
}
|
||||
|
||||
bool isAccessOperator(OverloadedOperatorKind OK) {
|
||||
return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
|
||||
OK == OO_Plus || OK == OO_PlusEqual || OK == OO_PlusPlus ||
|
||||
OK == OO_Subscript;
|
||||
}
|
||||
|
||||
bool isDecrementOperator(OverloadedOperatorKind OK) {
|
||||
return OK == OO_MinusEqual || OK == OO_MinusMinus;
|
||||
}
|
||||
|
||||
BinaryOperator::Opcode getOpcode(const SymExpr *SE) {
|
||||
if (const auto *BSE = dyn_cast<BinarySymExpr>(SE)) {
|
||||
return BSE->getOpcode();
|
||||
} else if (const auto *SC = dyn_cast<SymbolConjured>(SE)) {
|
||||
const auto *COE = dyn_cast<CXXOperatorCallExpr>(SC->getStmt());
|
||||
if (!COE)
|
||||
return BO_Comma; // Extremal value, neither EQ nor NE
|
||||
if (COE->getOperator() == OO_EqualEqual) {
|
||||
return BO_EQ;
|
||||
} else if (COE->getOperator() == OO_ExclaimEqual) {
|
||||
return BO_NE;
|
||||
}
|
||||
return BO_Comma; // Extremal value, neither EQ nor NE
|
||||
}
|
||||
return BO_Comma; // Extremal value, neither EQ nor NE
|
||||
}
|
||||
|
||||
const RegionOrSymbol getRegionOrSymbol(const SVal &Val) {
|
||||
if (const auto Reg = Val.getAsRegion()) {
|
||||
return Reg;
|
||||
} else if (const auto Sym = Val.getAsSymbol()) {
|
||||
return Sym;
|
||||
} else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
|
||||
return LCVal->getRegion();
|
||||
}
|
||||
return RegionOrSymbol();
|
||||
}
|
||||
|
||||
const ProgramStateRef processComparison(ProgramStateRef State,
|
||||
RegionOrSymbol LVal,
|
||||
RegionOrSymbol RVal, bool Equal) {
|
||||
const auto *LPos = getIteratorPosition(State, LVal);
|
||||
const auto *RPos = getIteratorPosition(State, RVal);
|
||||
if (LPos && !RPos) {
|
||||
State = adjustIteratorPosition(State, RVal, *LPos, Equal);
|
||||
} else if (!LPos && RPos) {
|
||||
State = adjustIteratorPosition(State, LVal, *RPos, Equal);
|
||||
} else if (LPos && RPos) {
|
||||
if (contradictingIteratorPositions(*LPos, *RPos, Equal)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return State;
|
||||
}
|
||||
|
||||
const ProgramStateRef saveComparison(ProgramStateRef State,
|
||||
const SymExpr *Condition, const SVal &LVal,
|
||||
const SVal &RVal, bool Eq) {
|
||||
const auto Left = getRegionOrSymbol(LVal);
|
||||
const auto Right = getRegionOrSymbol(RVal);
|
||||
if (!Left || !Right)
|
||||
return State;
|
||||
return State->set<IteratorComparisonMap>(Condition,
|
||||
IteratorComparison(Left, Right, Eq));
|
||||
}
|
||||
|
||||
const IteratorComparison *loadComparison(ProgramStateRef State,
|
||||
const SymExpr *Condition) {
|
||||
return State->get<IteratorComparisonMap>(Condition);
|
||||
}
|
||||
|
||||
const IteratorPosition *getIteratorPosition(ProgramStateRef State,
|
||||
const SVal &Val) {
|
||||
if (const auto Reg = Val.getAsRegion()) {
|
||||
return State->get<IteratorRegionMap>(Reg);
|
||||
} else if (const auto Sym = Val.getAsSymbol()) {
|
||||
return State->get<IteratorSymbolMap>(Sym);
|
||||
} else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
|
||||
return State->get<IteratorRegionMap>(LCVal->getRegion());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const IteratorPosition *getIteratorPosition(ProgramStateRef State,
|
||||
RegionOrSymbol RegOrSym) {
|
||||
if (RegOrSym.is<const MemRegion *>()) {
|
||||
return State->get<IteratorRegionMap>(RegOrSym.get<const MemRegion *>());
|
||||
} else if (RegOrSym.is<SymbolRef>()) {
|
||||
return State->get<IteratorSymbolMap>(RegOrSym.get<SymbolRef>());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
|
||||
IteratorPosition Pos) {
|
||||
if (const auto Reg = Val.getAsRegion()) {
|
||||
return State->set<IteratorRegionMap>(Reg, Pos);
|
||||
} else if (const auto Sym = Val.getAsSymbol()) {
|
||||
return State->set<IteratorSymbolMap>(Sym, Pos);
|
||||
} else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
|
||||
return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ProgramStateRef setIteratorPosition(ProgramStateRef State,
|
||||
RegionOrSymbol RegOrSym,
|
||||
IteratorPosition Pos) {
|
||||
if (RegOrSym.is<const MemRegion *>()) {
|
||||
return State->set<IteratorRegionMap>(RegOrSym.get<const MemRegion *>(),
|
||||
Pos);
|
||||
} else if (RegOrSym.is<SymbolRef>()) {
|
||||
return State->set<IteratorSymbolMap>(RegOrSym.get<SymbolRef>(), Pos);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
|
||||
RegionOrSymbol RegOrSym,
|
||||
IteratorPosition Pos, bool Equal) {
|
||||
|
||||
if ((Pos.isInRange() && Equal) || (Pos.isOutofRange() && !Equal)) {
|
||||
return setIteratorPosition(State, RegOrSym, IteratorPosition::getInRange());
|
||||
} else if (Pos.isOutofRange() && Equal) {
|
||||
return setIteratorPosition(State, RegOrSym,
|
||||
IteratorPosition::getOutofRange());
|
||||
} else {
|
||||
return State;
|
||||
}
|
||||
}
|
||||
|
||||
bool contradictingIteratorPositions(IteratorPosition Pos1,
|
||||
IteratorPosition Pos2, bool Equal) {
|
||||
return ((Pos1 != Pos2) && Equal) ||
|
||||
((Pos1.isOutofRange() && Pos2.isOutofRange()) && !Equal);
|
||||
}
|
||||
}
|
||||
|
||||
void ento::registerIteratorPastEndChecker(CheckerManager &Mgr) {
|
||||
Mgr.registerChecker<IteratorPastEndChecker>();
|
||||
}
|
@ -1248,7 +1248,14 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
|
||||
case Expr::MaterializeTemporaryExprClass: {
|
||||
Bldr.takeNodes(Pred);
|
||||
const MaterializeTemporaryExpr *MTE = cast<MaterializeTemporaryExpr>(S);
|
||||
CreateCXXTemporaryObject(MTE, Pred, Dst);
|
||||
ExplodedNodeSet dstPrevisit;
|
||||
getCheckerManager().runCheckersForPreStmt(dstPrevisit, Pred, MTE, *this);
|
||||
ExplodedNodeSet dstExpr;
|
||||
for (ExplodedNodeSet::iterator i = dstPrevisit.begin(),
|
||||
e = dstPrevisit.end(); i != e ; ++i) {
|
||||
CreateCXXTemporaryObject(MTE, *i, dstExpr);
|
||||
}
|
||||
getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, MTE, *this);
|
||||
Bldr.addNodes(Dst);
|
||||
break;
|
||||
}
|
||||
|
@ -10,6 +10,29 @@ typedef unsigned char uint8_t;
|
||||
typedef __typeof__(sizeof(int)) size_t;
|
||||
void *memmove(void *s1, const void *s2, size_t n);
|
||||
|
||||
template <typename T, typename Ptr, typename Ref> struct __iterator {
|
||||
typedef __iterator<T, T *, T &> iterator;
|
||||
typedef __iterator<T, const T *, const T &> const_iterator;
|
||||
|
||||
__iterator(const Ptr p) : ptr(p) {}
|
||||
|
||||
__iterator<T, Ptr, Ref> operator++() { return *this; }
|
||||
__iterator<T, Ptr, Ref> operator++(int) { return *this; }
|
||||
__iterator<T, Ptr, Ref> operator--() { return *this; }
|
||||
__iterator<T, Ptr, Ref> operator--(int) { return *this; }
|
||||
Ref operator*() const { return *ptr; }
|
||||
Ptr operator->() const { return *ptr; }
|
||||
|
||||
bool operator==(const iterator &rhs) const { return ptr == rhs.ptr; }
|
||||
bool operator==(const const_iterator &rhs) const { return ptr == rhs.ptr; }
|
||||
|
||||
bool operator!=(const iterator &rhs) const { return ptr != rhs.ptr; }
|
||||
bool operator!=(const const_iterator &rhs) const { return ptr != rhs.ptr; }
|
||||
|
||||
private:
|
||||
Ptr ptr;
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <class T1, class T2>
|
||||
struct pair {
|
||||
@ -27,6 +50,9 @@ namespace std {
|
||||
|
||||
template<typename T>
|
||||
class vector {
|
||||
typedef __iterator<T, T *, T &> iterator;
|
||||
typedef __iterator<T, const T *, const T &> const_iterator;
|
||||
|
||||
T *_start;
|
||||
T *_finish;
|
||||
T *_end_of_storage;
|
||||
@ -49,11 +75,10 @@ namespace std {
|
||||
return _start[n];
|
||||
}
|
||||
|
||||
T *begin() { return _start; }
|
||||
const T *begin() const { return _start; }
|
||||
|
||||
T *end() { return _finish; }
|
||||
const T *end() const { return _finish; }
|
||||
iterator begin() { return iterator(_start); }
|
||||
const_iterator begin() const { return const_iterator(_start); }
|
||||
iterator end() { return iterator(_finish); }
|
||||
const_iterator end() const { return const_iterator(_finish); }
|
||||
};
|
||||
|
||||
class exception {
|
||||
@ -223,6 +248,35 @@ namespace std {
|
||||
return __copy_backward(II, IE, OI);
|
||||
}
|
||||
|
||||
template <class InputIterator, class T>
|
||||
InputIterator find(InputIterator first, InputIterator last, const T &val);
|
||||
template <class ForwardIterator1, class ForwardIterator2>
|
||||
ForwardIterator1 find_end(ForwardIterator1 first1, ForwardIterator1 last1,
|
||||
ForwardIterator2 first2, ForwardIterator2 last2);
|
||||
template <class ForwardIterator1, class ForwardIterator2>
|
||||
ForwardIterator1 find_first_of(ForwardIterator1 first1,
|
||||
ForwardIterator1 last1,
|
||||
ForwardIterator2 first2,
|
||||
ForwardIterator2 last2);
|
||||
template <class InputIterator, class UnaryPredicate>
|
||||
InputIterator find_if(InputIterator first, InputIterator last,
|
||||
UnaryPredicate pred);
|
||||
template <class InputIterator, class UnaryPredicate>
|
||||
InputIterator find_if_not(InputIterator first, InputIterator last,
|
||||
UnaryPredicate pred);
|
||||
template <class InputIterator, class T>
|
||||
InputIterator lower_bound(InputIterator first, InputIterator last,
|
||||
const T &val);
|
||||
template <class InputIterator, class T>
|
||||
InputIterator upper_bound(InputIterator first, InputIterator last,
|
||||
const T &val);
|
||||
template <class ForwardIterator1, class ForwardIterator2>
|
||||
ForwardIterator1 search(ForwardIterator1 first1, ForwardIterator1 last1,
|
||||
ForwardIterator2 first2, ForwardIterator2 last2);
|
||||
template <class ForwardIterator1, class ForwardIterator2>
|
||||
ForwardIterator1 search_n(ForwardIterator1 first1, ForwardIterator1 last1,
|
||||
ForwardIterator2 first2, ForwardIterator2 last2);
|
||||
|
||||
struct input_iterator_tag { };
|
||||
struct output_iterator_tag { };
|
||||
struct forward_iterator_tag : public input_iterator_tag { };
|
||||
|
@ -18,6 +18,6 @@ class C {
|
||||
void testCopyNull(C *I, C *E) {
|
||||
std::copy(I, E, (C *)0);
|
||||
#ifndef SUPPRESSED
|
||||
// expected-warning@../Inputs/system-header-simulator-cxx.h:166 {{Called C++ object pointer is null}}
|
||||
// expected-warning@../Inputs/system-header-simulator-cxx.h:191 {{Called C++ object pointer is null}}
|
||||
#endif
|
||||
}
|
||||
|
@ -6,8 +6,7 @@
|
||||
void clang_analyzer_eval(bool);
|
||||
|
||||
void testVector(std::vector<int> &nums) {
|
||||
if (nums.begin()) return;
|
||||
if (nums.end()) return;
|
||||
if (nums.begin() != nums.end()) return;
|
||||
|
||||
clang_analyzer_eval(nums.size() == 0);
|
||||
#if INLINE
|
||||
|
205
test/Analysis/iterator-past-end.cpp
Normal file
205
test/Analysis/iterator-past-end.cpp
Normal file
@ -0,0 +1,205 @@
|
||||
// RUN: %clang_cc1 -std=c++11 -analyze -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorPastEnd -analyzer-eagerly-assume -analyzer-config c++-container-inlining=false %s -verify
|
||||
// RUN: %clang_cc1 -std=c++11 -analyze -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorPastEnd -analyzer-eagerly-assume -analyzer-config c++-container-inlining=true -DINLINE=1 %s -verify
|
||||
|
||||
#include "Inputs/system-header-simulator-cxx.h"
|
||||
|
||||
void simple_good(const std::vector<int> &v) {
|
||||
auto i = v.end();
|
||||
if (i != v.end())
|
||||
*i; // no-warning
|
||||
}
|
||||
|
||||
void simple_good_negated(const std::vector<int> &v) {
|
||||
auto i = v.end();
|
||||
if (!(i == v.end()))
|
||||
*i; // no-warning
|
||||
}
|
||||
|
||||
void simple_bad(const std::vector<int> &v) {
|
||||
auto i = v.end();
|
||||
*i; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
void copy(const std::vector<int> &v) {
|
||||
auto i1 = v.end();
|
||||
auto i2 = i1;
|
||||
*i2; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
void decrease(const std::vector<int> &v) {
|
||||
auto i = v.end();
|
||||
--i;
|
||||
*i; // no-warning
|
||||
}
|
||||
|
||||
void copy_and_decrease1(const std::vector<int> &v) {
|
||||
auto i1 = v.end();
|
||||
auto i2 = i1;
|
||||
--i1;
|
||||
*i1; // no-warning
|
||||
}
|
||||
|
||||
void copy_and_decrease2(const std::vector<int> &v) {
|
||||
auto i1 = v.end();
|
||||
auto i2 = i1;
|
||||
--i1;
|
||||
*i2; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
void copy_and_increase1(const std::vector<int> &v) {
|
||||
auto i1 = v.begin();
|
||||
auto i2 = i1;
|
||||
++i1;
|
||||
if (i1 == v.end())
|
||||
*i2; // no-warning
|
||||
}
|
||||
|
||||
void copy_and_increase2(const std::vector<int> &v) {
|
||||
auto i1 = v.begin();
|
||||
auto i2 = i1;
|
||||
++i1;
|
||||
if (i2 == v.end())
|
||||
*i2; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
void good_find(std::vector<int> &vec, int e) {
|
||||
auto first = std::find(vec.begin(), vec.end(), e);
|
||||
if (vec.end() != first)
|
||||
*first; // no-warning
|
||||
}
|
||||
|
||||
void bad_find(std::vector<int> &vec, int e) {
|
||||
auto first = std::find(vec.begin(), vec.end(), e);
|
||||
*first; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
void good_find_end(std::vector<int> &vec, std::vector<int> &seq) {
|
||||
auto last = std::find_end(vec.begin(), vec.end(), seq.begin(), seq.end());
|
||||
if (vec.end() != last)
|
||||
*last; // no-warning
|
||||
}
|
||||
|
||||
void bad_find_end(std::vector<int> &vec, std::vector<int> &seq) {
|
||||
auto last = std::find_end(vec.begin(), vec.end(), seq.begin(), seq.end());
|
||||
*last; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
void good_find_first_of(std::vector<int> &vec, std::vector<int> &seq) {
|
||||
auto first =
|
||||
std::find_first_of(vec.begin(), vec.end(), seq.begin(), seq.end());
|
||||
if (vec.end() != first)
|
||||
*first; // no-warning
|
||||
}
|
||||
|
||||
void bad_find_first_of(std::vector<int> &vec, std::vector<int> &seq) {
|
||||
auto first = std::find_end(vec.begin(), vec.end(), seq.begin(), seq.end());
|
||||
*first; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
bool odd(int i) { return i % 2; }
|
||||
|
||||
void good_find_if(std::vector<int> &vec) {
|
||||
auto first = std::find_if(vec.begin(), vec.end(), odd);
|
||||
if (vec.end() != first)
|
||||
*first; // no-warning
|
||||
}
|
||||
|
||||
void bad_find_if(std::vector<int> &vec, int e) {
|
||||
auto first = std::find_if(vec.begin(), vec.end(), odd);
|
||||
*first; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
void good_find_if_not(std::vector<int> &vec) {
|
||||
auto first = std::find_if_not(vec.begin(), vec.end(), odd);
|
||||
if (vec.end() != first)
|
||||
*first; // no-warning
|
||||
}
|
||||
|
||||
void bad_find_if_not(std::vector<int> &vec, int e) {
|
||||
auto first = std::find_if_not(vec.begin(), vec.end(), odd);
|
||||
*first; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
void good_lower_bound(std::vector<int> &vec, int e) {
|
||||
auto first = std::lower_bound(vec.begin(), vec.end(), e);
|
||||
if (vec.end() != first)
|
||||
*first; // no-warning
|
||||
}
|
||||
|
||||
void bad_lower_bound(std::vector<int> &vec, int e) {
|
||||
auto first = std::lower_bound(vec.begin(), vec.end(), e);
|
||||
*first; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
void good_upper_bound(std::vector<int> &vec, int e) {
|
||||
auto last = std::lower_bound(vec.begin(), vec.end(), e);
|
||||
if (vec.end() != last)
|
||||
*last; // no-warning
|
||||
}
|
||||
|
||||
void bad_upper_bound(std::vector<int> &vec, int e) {
|
||||
auto last = std::lower_bound(vec.begin(), vec.end(), e);
|
||||
*last; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
void good_search(std::vector<int> &vec, std::vector<int> &seq) {
|
||||
auto first = std::search(vec.begin(), vec.end(), seq.begin(), seq.end());
|
||||
if (vec.end() != first)
|
||||
*first; // no-warning
|
||||
}
|
||||
|
||||
void bad_search(std::vector<int> &vec, std::vector<int> &seq) {
|
||||
auto first = std::search(vec.begin(), vec.end(), seq.begin(), seq.end());
|
||||
*first; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
void good_search_n(std::vector<int> &vec, std::vector<int> &seq) {
|
||||
auto nth = std::search_n(vec.begin(), vec.end(), seq.begin(), seq.end());
|
||||
if (vec.end() != nth)
|
||||
*nth; // no-warning
|
||||
}
|
||||
|
||||
void bad_search_n(std::vector<int> &vec, std::vector<int> &seq) {
|
||||
auto nth = std::search_n(vec.begin(), vec.end(), seq.begin(), seq.end());
|
||||
*nth; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
template <class InputIterator, class T>
|
||||
InputIterator nonStdFind(InputIterator first, InputIterator last,
|
||||
const T &val) {
|
||||
for (auto i = first; i != last; ++i) {
|
||||
if (*i == val) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return last;
|
||||
}
|
||||
|
||||
void good_non_std_find(std::vector<int> &vec, int e) {
|
||||
auto first = nonStdFind(vec.begin(), vec.end(), e);
|
||||
if (vec.end() != first)
|
||||
*first; // no-warning
|
||||
}
|
||||
|
||||
void bad_non_std_find(std::vector<int> &vec, int e) {
|
||||
auto first = nonStdFind(vec.begin(), vec.end(), e);
|
||||
*first; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
void tricky(std::vector<int> &vec, int e) {
|
||||
const auto first = vec.begin();
|
||||
const auto comp1 = (first != vec.end()), comp2 = (first == vec.end());
|
||||
if (comp1)
|
||||
*first;
|
||||
}
|
||||
|
||||
void loop(std::vector<int> &vec, int e) {
|
||||
auto start = vec.begin();
|
||||
while (true) {
|
||||
auto item = std::find(start, vec.end(), e);
|
||||
if (item == vec.end())
|
||||
break;
|
||||
*item; // no-warning
|
||||
start = ++item; // no-warning
|
||||
}
|
||||
}
|
@ -28,14 +28,23 @@ namespace default_ctor {
|
||||
struct C;
|
||||
struct D;
|
||||
|
||||
struct convert_to_D1 {
|
||||
operator D&&();
|
||||
};
|
||||
struct convert_to_D2 {
|
||||
operator D&&();
|
||||
};
|
||||
|
||||
struct A { // expected-note 4{{candidate}}
|
||||
A(); // expected-note {{candidate}}
|
||||
|
||||
A(C &&); // expected-note {{candidate}}
|
||||
C &operator=(C&&); // expected-note {{candidate}}
|
||||
|
||||
A(D &&); // expected-note {{candidate}}
|
||||
A(D &&);
|
||||
D &operator=(D&&); // expected-note {{candidate}}
|
||||
|
||||
A(convert_to_D2); // expected-note {{candidate}}
|
||||
};
|
||||
|
||||
struct B { // expected-note 4{{candidate}}
|
||||
@ -44,8 +53,10 @@ namespace default_ctor {
|
||||
B(C &&); // expected-note {{candidate}}
|
||||
C &operator=(C&&); // expected-note {{candidate}}
|
||||
|
||||
B(D &&); // expected-note {{candidate}}
|
||||
B(D &&);
|
||||
D &operator=(D&&); // expected-note {{candidate}}
|
||||
|
||||
B(convert_to_D2); // expected-note {{candidate}}
|
||||
};
|
||||
|
||||
struct C : A, B {
|
||||
@ -75,7 +86,20 @@ namespace default_ctor {
|
||||
// versions are inherited.
|
||||
D d; // expected-error {{ambiguous}}
|
||||
void f(D d) {
|
||||
D d2(static_cast<D&&>(d)); // expected-error {{ambiguous}}
|
||||
D d2(static_cast<D&&>(d)); // ok, ignores inherited constructors
|
||||
D d3(convert_to_D1{}); // ok, ignores inherited constructors
|
||||
D d4(convert_to_D2{}); // expected-error {{ambiguous}}
|
||||
d = static_cast<D&&>(d); // expected-error {{ambiguous}}
|
||||
}
|
||||
|
||||
struct Y;
|
||||
struct X { // expected-note 2{{candidate}}
|
||||
X();
|
||||
X(volatile Y &); // expected-note {{constructor inherited from base class cannot be used to initialize from an argument of the derived class type}}
|
||||
} x;
|
||||
struct Y : X { using X::X; } volatile y; // expected-note 2{{candidate}}
|
||||
struct Z : Y { using Y::Y; } volatile z; // expected-note 3{{candidate}} expected-note 5{{inherited here}}
|
||||
Z z1(x); // ok
|
||||
Z z2(y); // ok, Z is not reference-related to type of y
|
||||
Z z3(z); // expected-error {{no match}}
|
||||
}
|
||||
|
@ -174,3 +174,133 @@ namespace dr1359 { // dr1359: 3.5
|
||||
constexpr Y y = Y(); // expected-error {{no matching}}
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace dr1388 { // dr1388: 4.0
|
||||
template<typename A, typename ...T> void f(T..., A); // expected-note 1+{{candidate}} expected-error 0-1{{C++11}}
|
||||
template<typename ...T> void g(T..., int); // expected-note 1+{{candidate}} expected-error 0-1{{C++11}}
|
||||
template<typename ...T, typename A> void h(T..., A); // expected-note 1+{{candidate}} expected-error 0-1{{C++11}}
|
||||
|
||||
void test_f() {
|
||||
f(0); // ok, trailing parameter pack deduced to empty
|
||||
f(0, 0); // expected-error {{no matching}}
|
||||
f<int>(0);
|
||||
f<int>(0, 0); // expected-error {{no matching}}
|
||||
f<int, int>(0, 0);
|
||||
f<int, int, int>(0, 0); // expected-error {{no matching}}
|
||||
|
||||
g(0);
|
||||
g(0, 0); // expected-error {{no matching}}
|
||||
g<>(0);
|
||||
g<int>(0); // expected-error {{no matching}}
|
||||
g<int>(0, 0);
|
||||
|
||||
h(0);
|
||||
h(0, 0); // expected-error {{no matching}}
|
||||
h<int>(0, 0);
|
||||
h<int, int>(0, 0); // expected-error {{no matching}}
|
||||
}
|
||||
|
||||
// A non-trailing parameter pack is still a non-deduced context, even though
|
||||
// we know exactly how many arguments correspond to it.
|
||||
template<typename T, typename U> struct pair {};
|
||||
template<typename ...T> struct tuple { typedef char type; }; // expected-error 0-2{{C++11}}
|
||||
template<typename ...T, typename ...U> void f_pair_1(pair<T, U>..., int); // expected-error 0-2{{C++11}} expected-note {{different lengths (2 vs. 0)}}
|
||||
template<typename ...T, typename U> void f_pair_2(pair<T, char>..., U); // expected-error 0-2{{C++11}}
|
||||
template<typename ...T, typename ...U> void f_pair_3(pair<T, U>..., tuple<U...>); // expected-error 0-2{{C++11}} expected-note {{different lengths (2 vs. 1)}}
|
||||
template<typename ...T> void f_pair_4(pair<T, char>..., T...); // expected-error 0-2{{C++11}} expected-note {{<int, long> vs. <int, long, const char *>}}
|
||||
void g(pair<int, char> a, pair<long, char> b, tuple<char, char> c) {
|
||||
f_pair_1<int, long>(a, b, 0); // expected-error {{no match}}
|
||||
f_pair_2<int, long>(a, b, 0);
|
||||
f_pair_3<int, long>(a, b, c);
|
||||
f_pair_3<int, long>(a, b, tuple<char>()); // expected-error {{no match}}
|
||||
f_pair_4<int, long>(a, b, 0, 0L);
|
||||
f_pair_4<int, long>(a, b, 0, 0L, "foo"); // expected-error {{no match}}
|
||||
}
|
||||
}
|
||||
|
||||
namespace dr1391 { // dr1391: partial
|
||||
struct A {}; struct B : A {};
|
||||
template<typename T> struct C { C(int); typename T::error error; }; // expected-error 2{{'::'}}
|
||||
template<typename T> struct D {};
|
||||
|
||||
// No deduction is performed for parameters with no deducible template-parameters, therefore types do not need to match.
|
||||
template<typename T> void a(T, int T::*);
|
||||
void test_a(int A::*p) { a(A(), p); } // ok, type of second parameter does not need to match
|
||||
|
||||
namespace dr_example_1 {
|
||||
template<typename T, typename U> void f(C<T>);
|
||||
template<typename T> void f(D<T>);
|
||||
|
||||
void g(D<int> d) {
|
||||
f(d); // ok, first 'f' eliminated by deduction failure
|
||||
f<int>(d); // ok, first 'f' eliminated because 'U' cannot be deduced
|
||||
}
|
||||
}
|
||||
|
||||
namespace dr_example_2 {
|
||||
template<typename T> typename C<T>::error f(int, T);
|
||||
template<typename T> T f(T, T);
|
||||
|
||||
void g(A a) {
|
||||
f(a, a); // ok, no conversion from A to int for first parameter of first candidate
|
||||
}
|
||||
}
|
||||
|
||||
namespace std_example {
|
||||
template<typename T> struct Z {
|
||||
typedef typename T::x xx;
|
||||
};
|
||||
template<typename T> typename Z<T>::xx f(void *, T);
|
||||
template<typename T> void f(int, T);
|
||||
struct A {} a;
|
||||
void g() { f(1, a); }
|
||||
}
|
||||
|
||||
template<typename T> void b(C<int> ci, T *p);
|
||||
void b(...);
|
||||
void test_b() {
|
||||
b(0, 0); // ok, deduction fails prior to forming a conversion sequence and instantiating C<int>
|
||||
// FIXME: The "while substituting" note should point at the overload candidate.
|
||||
b<int>(0, 0); // expected-note {{instantiation of}} expected-note {{while substituting}}
|
||||
}
|
||||
|
||||
template<typename T> struct Id { typedef T type; };
|
||||
template<typename T> void c(T, typename Id<C<T> >::type);
|
||||
void test_c() {
|
||||
// Implicit conversion sequences for dependent types are checked later.
|
||||
c(0.0, 0); // expected-note {{instantiation of}}
|
||||
}
|
||||
|
||||
namespace partial_ordering {
|
||||
// FIXME: Second template should be considered more specialized because non-dependent parameter is ignored.
|
||||
template<typename T> int a(T, short) = delete; // expected-error 0-1{{extension}} expected-note {{candidate}}
|
||||
template<typename T> int a(T*, char); // expected-note {{candidate}}
|
||||
int test_a = a((int*)0, 0); // FIXME: expected-error {{ambiguous}}
|
||||
|
||||
// FIXME: Second template should be considered more specialized:
|
||||
// deducing #1 from #2 ignores the second P/A pair, so deduction succeeds,
|
||||
// deducing #2 from #1 fails to deduce T, so deduction fails.
|
||||
template<typename T> int b(T, int) = delete; // expected-error 0-1{{extension}} expected-note {{candidate}}
|
||||
template<typename T, typename U> int b(T*, U); // expected-note {{candidate}}
|
||||
int test_b = b((int*)0, 0); // FIXME: expected-error {{ambiguous}}
|
||||
|
||||
// Unintended consequences: because partial ordering does not consider
|
||||
// explicit template arguments, and deduction from a non-dependent type
|
||||
// vacuously succeeds, a non-dependent template is less specialized than
|
||||
// anything else!
|
||||
// According to DR1391, this is ambiguous!
|
||||
template<typename T> int c(int);
|
||||
template<typename T> int c(T);
|
||||
int test_c1 = c(0); // ok
|
||||
int test_c2 = c<int>(0); // FIXME: apparently ambiguous
|
||||
}
|
||||
}
|
||||
|
||||
namespace dr1399 { // dr1399: dup 1388
|
||||
template<typename ...T> void f(T..., int, T...) {} // expected-note {{candidate}} expected-error 0-1{{C++11}}
|
||||
void g() {
|
||||
f(0);
|
||||
f<int>(0, 0, 0);
|
||||
f(0, 0, 0); // expected-error {{no match}}
|
||||
}
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ namespace dr1959 { // dr1959: 3.9
|
||||
a() = default;
|
||||
a(const a &) = delete; // expected-note 2{{deleted}}
|
||||
a(const b &) = delete; // not inherited
|
||||
a(c &&) = delete; // expected-note {{deleted}}
|
||||
a(c &&) = delete;
|
||||
template<typename T> a(T) = delete;
|
||||
};
|
||||
|
||||
@ -152,13 +152,14 @@ namespace dr1959 { // dr1959: 3.9
|
||||
b y = x; // expected-error {{deleted}}
|
||||
b z = z; // expected-error {{deleted}}
|
||||
|
||||
// FIXME: It's not really clear that this matches the intent, but it's
|
||||
// consistent with the behavior for assignment operators.
|
||||
struct c : a {
|
||||
using a::a;
|
||||
c(const c &);
|
||||
};
|
||||
c q(static_cast<c&&>(q)); // expected-error {{call to deleted}}
|
||||
// FIXME: As a resolution to an open DR against P0136R0, we disallow
|
||||
// use of inherited constructors to construct from a single argument
|
||||
// where the derived class is reference-related to its type.
|
||||
c q(static_cast<c&&>(q));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -139,11 +139,11 @@ namespace NonLocalLambdaInstantation {
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct X2 { // expected-note{{in instantiation of default member initializer 'NonLocalLambdaInstantation::X2<int *>::x' requested here}}
|
||||
struct X2 {
|
||||
int x = []{ return T(); }(); // expected-error{{cannot initialize a member subobject of type 'int' with an rvalue of type 'int *'}}
|
||||
};
|
||||
|
||||
X2<int> x2i;
|
||||
X2<float> x2f;
|
||||
X2<int*> x2ip; // expected-note{{implicit default constructor for 'NonLocalLambdaInstantation::X2<int *>' first required here}}
|
||||
X2<int*> x2ip; // expected-note{{in instantiation of default member initializer 'NonLocalLambdaInstantation::X2<int *>::x'}}
|
||||
}
|
||||
|
@ -76,14 +76,17 @@ void test_pair_deduction(int *ip, float *fp, double *dp) {
|
||||
first_arg_pair(make_pair(ip, 17), 16); // expected-error{{no matching function for call to 'first_arg_pair'}}
|
||||
}
|
||||
|
||||
// For a function parameter pack that does not occur at the end of the
|
||||
// parameter-declaration-list, the type of the parameter pack is a
|
||||
// non-deduced context.
|
||||
// A function parameter pack not at the end of the parameter list is never
|
||||
// deduced. We interpret this as meaning the types within it are never
|
||||
// deduced, and thus must match explicitly-specified values.
|
||||
template<typename ...Types> struct tuple { };
|
||||
|
||||
template<typename ...Types>
|
||||
void pack_not_at_end(tuple<Types...>, Types... values, int);
|
||||
void pack_not_at_end(tuple<Types...>, Types... values, int); // expected-note {{<int *, double *> vs. <>}}
|
||||
|
||||
void test_pack_not_at_end(tuple<int*, double*> t2) {
|
||||
pack_not_at_end(t2, 0, 0, 0);
|
||||
pack_not_at_end(t2, 0, 0, 0); // expected-error {{no match}}
|
||||
// FIXME: Should the "original argument type must match deduced parameter
|
||||
// type" rule apply here?
|
||||
pack_not_at_end<int*, double*>(t2, 0, 0, 0); // ok
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
// RUN: %clang_cc1 -verify %s -std=c++11
|
||||
// RUN: %clang_cc1 -verify %s -std=c++14
|
||||
|
||||
template<const int I> struct S { // expected-note {{instantiation}}
|
||||
template<const int I> struct S {
|
||||
decltype(I) n;
|
||||
int &&r = I; // expected-warning 2{{binding reference member 'r' to a temporary value}} expected-note 2{{declared here}}
|
||||
};
|
||||
S<5> s;
|
||||
S<5> s; // expected-note {{instantiation}}
|
||||
|
||||
template<typename T, T v> struct U { // expected-note {{instantiation}}
|
||||
template<typename T, T v> struct U {
|
||||
decltype(v) n;
|
||||
int &&r = v; // expected-warning {{binding reference member 'r' to a temporary value}} expected-note {{declared here}}
|
||||
};
|
||||
U<const int, 6> u;
|
||||
U<const int, 6> u; // expected-note {{instantiation}}
|
||||
|
@ -1,4 +1,6 @@
|
||||
// RUN: %clang -S -emit-llvm -o - -O2 %s | FileCheck %s -check-prefixes=CHECK,O2
|
||||
// RUN: %clang -S -emit-llvm -o - -O2 -Xclang -disable-lifetime-markers %s \
|
||||
// RUN: | FileCheck %s -check-prefixes=CHECK,O0
|
||||
// RUN: %clang -S -emit-llvm -o - -O0 %s | FileCheck %s -check-prefixes=CHECK,O0
|
||||
|
||||
extern int bar(char *A, int n);
|
||||
|
@ -12,6 +12,14 @@
|
||||
; RUN: %clang -O2 -o %t4.o -x ir %t1.o -c -fthinlto-index=bad.thinlto.bc 2>&1 | FileCheck %s -check-prefix=CHECK-ERROR1
|
||||
; CHECK-ERROR1: Error loading index file 'bad.thinlto.bc'
|
||||
|
||||
; Ensure we ignore empty index file under -ignore-empty-index-file, and run
|
||||
; non-ThinLTO compilation which would not import f2
|
||||
; RUN: touch %t4.thinlto.bc
|
||||
; RUN: %clang -target x86_64-unknown-linux-gnu -O2 -o %t4.o -x ir %t1.o -c -fthinlto-index=%t4.thinlto.bc -mllvm -ignore-empty-index-file
|
||||
; RUN: llvm-nm %t4.o | FileCheck --check-prefix=CHECK-OBJ-IGNORE-EMPTY %s
|
||||
; CHECK-OBJ-IGNORE-EMPTY: T f1
|
||||
; CHECK-OBJ-IGNORE-EMPTY: U f2
|
||||
|
||||
; Ensure f2 was imported
|
||||
; RUN: %clang -target x86_64-unknown-linux-gnu -O2 -o %t3.o -x ir %t1.o -c -fthinlto-index=%t.thinlto.bc
|
||||
; RUN: llvm-nm %t3.o | FileCheck --check-prefix=CHECK-OBJ %s
|
||||
|
@ -1,4 +1,5 @@
|
||||
// RUN: %clang_cc1 %s -triple=thumbv7-apple-ios6.0 -fno-use-cxa-atexit -target-abi apcs-gnu -emit-llvm -o - -fexceptions | FileCheck %s
|
||||
// RUN: %clang_cc1 %s -triple=thumbv7-apple-ios6.0 -fno-use-cxa-atexit -target-abi apcs-gnu -emit-llvm -std=gnu++98 -o - -fexceptions | FileCheck -check-prefix=CHECK -check-prefix=CHECK98 %s
|
||||
// RUN: %clang_cc1 %s -triple=thumbv7-apple-ios6.0 -fno-use-cxa-atexit -target-abi apcs-gnu -emit-llvm -std=gnu++11 -o - -fexceptions | FileCheck -check-prefix=CHECK -check-prefix=CHECK11 %s
|
||||
|
||||
// CHECK: @_ZZN5test74testEvE1x = internal global i32 0, align 4
|
||||
// CHECK: @_ZGVZN5test74testEvE1x = internal global i32 0
|
||||
@ -156,7 +157,8 @@ namespace test3 {
|
||||
// CHECK: getelementptr {{.*}}, i32 4
|
||||
// CHECK: bitcast {{.*}} to i32*
|
||||
// CHECK: load
|
||||
// CHECK: invoke {{.*}} @_ZN5test31AD1Ev
|
||||
// CHECK98: invoke {{.*}} @_ZN5test31AD1Ev
|
||||
// CHECK11: call {{.*}} @_ZN5test31AD1Ev
|
||||
// CHECK: call void @_ZdaPv
|
||||
delete [] x;
|
||||
}
|
||||
@ -168,7 +170,8 @@ namespace test3 {
|
||||
// CHECK: getelementptr {{.*}}, i32 4
|
||||
// CHECK: bitcast {{.*}} to i32*
|
||||
// CHECK: load
|
||||
// CHECK: invoke {{.*}} @_ZN5test31AD1Ev
|
||||
// CHECK98: invoke {{.*}} @_ZN5test31AD1Ev
|
||||
// CHECK11: call {{.*}} @_ZN5test31AD1Ev
|
||||
// CHECK: call void @_ZdaPv
|
||||
delete [] x;
|
||||
}
|
||||
|
@ -83,12 +83,17 @@ int main(int argc, char **argv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown_unknown -emit-llvm -debug-info-kind=limited -fexceptions %s -o - | FileCheck %s
|
||||
// RUN: %clang_cc1 -triple i686-cygwin -emit-llvm -debug-info-kind=limited -fexceptions %s -o - | FileCheck %s
|
||||
// RUN: %clang_cc1 -triple armv7l-unknown-linux-gnueabihf -emit-llvm -debug-info-kind=limited -fexceptions %s -o - | FileCheck %s
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown_unknown -emit-llvm -debug-info-kind=limited -fexceptions -std=c++98 %s -o - | FileCheck -check-prefix=CHECK98 %s
|
||||
// RUN: %clang_cc1 -triple i686-cygwin -emit-llvm -debug-info-kind=limited -fexceptions -std=c++98 %s -o - | FileCheck -check-prefix=CHECK98 %s
|
||||
// RUN: %clang_cc1 -triple armv7l-unknown-linux-gnueabihf -emit-llvm -debug-info-kind=limited -fexceptions -std=c++98 %s -o - | FileCheck -check-prefix=CHECK98 %s
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown_unknown -emit-llvm -debug-info-kind=limited -fexceptions -std=c++11 %s -o - | FileCheck -check-prefix=CHECK11 %s
|
||||
// RUN: %clang_cc1 -triple i686-cygwin -emit-llvm -debug-info-kind=limited -fexceptions -std=c++11 %s -o - | FileCheck -check-prefix=CHECK11 %s
|
||||
// RUN: %clang_cc1 -triple armv7l-unknown-linux-gnueabihf -emit-llvm -debug-info-kind=limited -fexceptions -std=c++11 %s -o - | FileCheck -check-prefix=CHECK11 %s
|
||||
|
||||
// CHECK98: invoke {{.+}} @_ZN1BD1Ev(%class.B* %b)
|
||||
// CHECK98-NEXT: unwind label %{{.+}}, !dbg ![[EXCEPTLOC:.*]]
|
||||
// CHECK11: call {{.+}} @_ZN1BD1Ev(%class.B* %b){{.*}}, !dbg ![[EXCEPTLOC:.*]]
|
||||
|
||||
// CHECK: invoke {{.+}} @_ZN1BD1Ev(%class.B* %b)
|
||||
// CHECK-NEXT: unwind label %{{.+}}, !dbg ![[EXCEPTLOC:.*]]
|
||||
// CHECK: store i32 0, i32* %{{.+}}, !dbg ![[RETLOC:.*]]
|
||||
|
||||
// CHECK: [[F:![0-9]*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "F"
|
||||
|
82
test/CodeGenCXX/dllexport-ctor-closure.cpp
Normal file
82
test/CodeGenCXX/dllexport-ctor-closure.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
// RUN: %clang_cc1 -triple i686-windows-msvc -emit-llvm -std=c++14 \
|
||||
// RUN: -fno-threadsafe-statics -fms-extensions -O1 -mconstructor-aliases \
|
||||
// RUN: -disable-llvm-passes -o - %s -w -fms-compatibility-version=19.00 | \
|
||||
// RUN: FileCheck %s
|
||||
|
||||
struct CtorWithClosure {
|
||||
__declspec(dllexport) CtorWithClosure(...) {}
|
||||
// CHECK-LABEL: define weak_odr dllexport x86_thiscallcc void @"\01??_FCtorWithClosure@@QAEXXZ"({{.*}}) {{#[0-9]+}} comdat
|
||||
// CHECK: %[[this_addr:.*]] = alloca %struct.CtorWithClosure*, align 4
|
||||
// CHECK: store %struct.CtorWithClosure* %this, %struct.CtorWithClosure** %[[this_addr]], align 4
|
||||
// CHECK: %[[this:.*]] = load %struct.CtorWithClosure*, %struct.CtorWithClosure** %[[this_addr]]
|
||||
// CHECK: call %struct.CtorWithClosure* (%struct.CtorWithClosure*, ...) @"\01??0CtorWithClosure@@QAA@ZZ"(%struct.CtorWithClosure* %[[this]])
|
||||
// CHECK: ret void
|
||||
};
|
||||
|
||||
struct CtorWithClosureOutOfLine {
|
||||
__declspec(dllexport) CtorWithClosureOutOfLine(...);
|
||||
};
|
||||
CtorWithClosureOutOfLine::CtorWithClosureOutOfLine(...) {}
|
||||
// CHECK-LABEL: define weak_odr dllexport x86_thiscallcc void @"\01??_FCtorWithClosureOutOfLine@@QAEXXZ"({{.*}}) {{#[0-9]+}} comdat
|
||||
|
||||
#define DELETE_IMPLICIT_MEMBERS(ClassName) \
|
||||
ClassName(ClassName &&) = delete; \
|
||||
ClassName(ClassName &) = delete; \
|
||||
~ClassName() = delete; \
|
||||
ClassName &operator=(ClassName &) = delete
|
||||
|
||||
struct __declspec(dllexport) ClassWithClosure {
|
||||
DELETE_IMPLICIT_MEMBERS(ClassWithClosure);
|
||||
ClassWithClosure(...) {}
|
||||
// CHECK-LABEL: define weak_odr dllexport x86_thiscallcc void @"\01??_FClassWithClosure@@QAEXXZ"({{.*}}) {{#[0-9]+}} comdat
|
||||
// CHECK: %[[this_addr:.*]] = alloca %struct.ClassWithClosure*, align 4
|
||||
// CHECK: store %struct.ClassWithClosure* %this, %struct.ClassWithClosure** %[[this_addr]], align 4
|
||||
// CHECK: %[[this:.*]] = load %struct.ClassWithClosure*, %struct.ClassWithClosure** %[[this_addr]]
|
||||
// CHECK: call %struct.ClassWithClosure* (%struct.ClassWithClosure*, ...) @"\01??0ClassWithClosure@@QAA@ZZ"(%struct.ClassWithClosure* %[[this]])
|
||||
// CHECK: ret void
|
||||
};
|
||||
|
||||
template <typename T> struct TemplateWithClosure {
|
||||
TemplateWithClosure(int x = sizeof(T)) {}
|
||||
};
|
||||
extern template struct TemplateWithClosure<char>;
|
||||
template struct __declspec(dllexport) TemplateWithClosure<char>;
|
||||
extern template struct TemplateWithClosure<int>;
|
||||
template struct __declspec(dllexport) TemplateWithClosure<int>;
|
||||
|
||||
// CHECK-LABEL: define weak_odr dllexport x86_thiscallcc void @"\01??_F?$TemplateWithClosure@D@@QAEXXZ"({{.*}}) {{#[0-9]+}} comdat
|
||||
// CHECK: call {{.*}} @"\01??0?$TemplateWithClosure@D@@QAE@H@Z"({{.*}}, i32 1)
|
||||
|
||||
// CHECK-LABEL: define weak_odr dllexport x86_thiscallcc void @"\01??_F?$TemplateWithClosure@H@@QAEXXZ"({{.*}}) {{#[0-9]+}} comdat
|
||||
// CHECK: call {{.*}} @"\01??0?$TemplateWithClosure@H@@QAE@H@Z"({{.*}}, i32 4)
|
||||
|
||||
struct __declspec(dllexport) NestedOuter {
|
||||
DELETE_IMPLICIT_MEMBERS(NestedOuter);
|
||||
NestedOuter(void *p = 0) {}
|
||||
struct __declspec(dllexport) NestedInner {
|
||||
DELETE_IMPLICIT_MEMBERS(NestedInner);
|
||||
NestedInner(void *p = 0) {}
|
||||
};
|
||||
};
|
||||
|
||||
// CHECK-LABEL: define weak_odr dllexport x86_thiscallcc void @"\01??_FNestedOuter@@QAEXXZ"({{.*}}) {{#[0-9]+}} comdat
|
||||
// CHECK-LABEL: define weak_odr dllexport x86_thiscallcc void @"\01??_FNestedInner@NestedOuter@@QAEXXZ"({{.*}}) {{#[0-9]+}} comdat
|
||||
|
||||
struct HasDtor {
|
||||
~HasDtor();
|
||||
int o;
|
||||
};
|
||||
struct HasImplicitDtor1 { HasDtor o; };
|
||||
struct HasImplicitDtor2 { HasDtor o; };
|
||||
struct __declspec(dllexport) CtorClosureInline {
|
||||
CtorClosureInline(const HasImplicitDtor1 &v = {}) {}
|
||||
};
|
||||
struct __declspec(dllexport) CtorClosureOutOfLine {
|
||||
CtorClosureOutOfLine(const HasImplicitDtor2 &v = {});
|
||||
};
|
||||
CtorClosureOutOfLine::CtorClosureOutOfLine(const HasImplicitDtor2 &v) {}
|
||||
|
||||
// CHECK-LABEL: define weak_odr dllexport x86_thiscallcc void @"\01??_FCtorClosureInline@@QAEXXZ"
|
||||
// CHECK-LABEL: define linkonce_odr x86_thiscallcc void @"\01??1HasImplicitDtor1@@QAE@XZ"
|
||||
// CHECK-LABEL: define weak_odr dllexport x86_thiscallcc void @"\01??_FCtorClosureOutOfLine@@QAEXXZ"
|
||||
// CHECK-LABEL: define linkonce_odr x86_thiscallcc void @"\01??1HasImplicitDtor2@@QAE@XZ"
|
@ -488,57 +488,6 @@ struct S {
|
||||
};
|
||||
};
|
||||
|
||||
struct CtorWithClosure {
|
||||
__declspec(dllexport) CtorWithClosure(...) {}
|
||||
// M32-DAG: define weak_odr dllexport x86_thiscallcc void @"\01??_FCtorWithClosure@@QAEXXZ"({{.*}}) {{#[0-9]+}} comdat
|
||||
// M32-DAG: %[[this_addr:.*]] = alloca %struct.CtorWithClosure*, align 4
|
||||
// M32-DAG: store %struct.CtorWithClosure* %this, %struct.CtorWithClosure** %[[this_addr]], align 4
|
||||
// M32-DAG: %[[this:.*]] = load %struct.CtorWithClosure*, %struct.CtorWithClosure** %[[this_addr]]
|
||||
// M32-DAG: call %struct.CtorWithClosure* (%struct.CtorWithClosure*, ...) @"\01??0CtorWithClosure@@QAA@ZZ"(%struct.CtorWithClosure* %[[this]])
|
||||
// M32-DAG: ret void
|
||||
};
|
||||
|
||||
#define DELETE_IMPLICIT_MEMBERS(ClassName) \
|
||||
ClassName(ClassName &&) = delete; \
|
||||
ClassName(ClassName &) = delete; \
|
||||
~ClassName() = delete; \
|
||||
ClassName &operator=(ClassName &) = delete
|
||||
|
||||
struct __declspec(dllexport) ClassWithClosure {
|
||||
DELETE_IMPLICIT_MEMBERS(ClassWithClosure);
|
||||
ClassWithClosure(...) {}
|
||||
// M32-DAG: define weak_odr dllexport x86_thiscallcc void @"\01??_FClassWithClosure@@QAEXXZ"({{.*}}) {{#[0-9]+}} comdat
|
||||
// M32-DAG: %[[this_addr:.*]] = alloca %struct.ClassWithClosure*, align 4
|
||||
// M32-DAG: store %struct.ClassWithClosure* %this, %struct.ClassWithClosure** %[[this_addr]], align 4
|
||||
// M32-DAG: %[[this:.*]] = load %struct.ClassWithClosure*, %struct.ClassWithClosure** %[[this_addr]]
|
||||
// M32-DAG: call %struct.ClassWithClosure* (%struct.ClassWithClosure*, ...) @"\01??0ClassWithClosure@@QAA@ZZ"(%struct.ClassWithClosure* %[[this]])
|
||||
// M32-DAG: ret void
|
||||
};
|
||||
|
||||
template <typename T> struct TemplateWithClosure {
|
||||
TemplateWithClosure(int x = sizeof(T)) {}
|
||||
};
|
||||
extern template struct TemplateWithClosure<char>;
|
||||
template struct __declspec(dllexport) TemplateWithClosure<char>;
|
||||
extern template struct TemplateWithClosure<int>;
|
||||
template struct __declspec(dllexport) TemplateWithClosure<int>;
|
||||
// M32-DAG: define weak_odr dllexport x86_thiscallcc void @"\01??_F?$TemplateWithClosure@D@@QAEXXZ"({{.*}}) {{#[0-9]+}} comdat
|
||||
// M32-DAG: call {{.*}} @"\01??0?$TemplateWithClosure@D@@QAE@H@Z"({{.*}}, i32 1)
|
||||
// M32-DAG: define weak_odr dllexport x86_thiscallcc void @"\01??_F?$TemplateWithClosure@H@@QAEXXZ"({{.*}}) {{#[0-9]+}} comdat
|
||||
// M32-DAG: call {{.*}} @"\01??0?$TemplateWithClosure@H@@QAE@H@Z"({{.*}}, i32 4)
|
||||
|
||||
struct __declspec(dllexport) NestedOuter {
|
||||
DELETE_IMPLICIT_MEMBERS(NestedOuter);
|
||||
NestedOuter(void *p = 0) {}
|
||||
struct __declspec(dllexport) NestedInner {
|
||||
DELETE_IMPLICIT_MEMBERS(NestedInner);
|
||||
NestedInner(void *p = 0) {}
|
||||
};
|
||||
};
|
||||
|
||||
// M32-DAG: define weak_odr dllexport x86_thiscallcc void @"\01??_FNestedOuter@@QAEXXZ"({{.*}}) {{#[0-9]+}} comdat
|
||||
// M32-DAG: define weak_odr dllexport x86_thiscallcc void @"\01??_FNestedInner@NestedOuter@@QAEXXZ"({{.*}}) {{#[0-9]+}} comdat
|
||||
|
||||
template <typename T>
|
||||
struct SomeTemplate {
|
||||
SomeTemplate(T o = T()) : o(o) {}
|
||||
|
@ -1,7 +1,8 @@
|
||||
// Check that in case of copying an array of memcpy-able objects, their
|
||||
// destructors will be called if an exception is thrown.
|
||||
//
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fexceptions -fcxx-exceptions -O0 -fno-elide-constructors -emit-llvm %s -o - | FileCheck %s
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fexceptions -fcxx-exceptions -O0 -fno-elide-constructors -std=c++98 -emit-llvm %s -o - | FileCheck -check-prefix=CHECK -check-prefix=CHECK98 %s
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fexceptions -fcxx-exceptions -O0 -fno-elide-constructors -std=c++11 -emit-llvm %s -o - | FileCheck -check-prefix=CHECK -check-prefix=CHECK11 %s
|
||||
|
||||
struct ImplicitCopy {
|
||||
int x;
|
||||
@ -25,7 +26,8 @@ int main () {
|
||||
// CHECK_LABEL: main
|
||||
// CHECK-NOT: call void @_ZN9ThrowCopyC1ERKS_
|
||||
// CHECK: invoke void @_ZN9ThrowCopyC1ERKS_
|
||||
// CHECK: invoke void @_ZN12ImplicitCopyD1Ev
|
||||
// CHECK98: invoke void @_ZN12ImplicitCopyD1Ev
|
||||
// CHECK11: call void @_ZN12ImplicitCopyD1Ev
|
||||
Container c2(c1);
|
||||
}
|
||||
catch (...) {
|
||||
|
@ -1,4 +1,5 @@
|
||||
// RUN: %clang_cc1 %s -triple=x86_64-linux-gnu -emit-llvm -o - -fcxx-exceptions -fexceptions | FileCheck %s
|
||||
// RUN: %clang_cc1 %s -triple=x86_64-linux-gnu -emit-llvm -std=c++98 -o - -fcxx-exceptions -fexceptions | FileCheck -check-prefix=CHECK -check-prefix=CHECK98 %s
|
||||
// RUN: %clang_cc1 %s -triple=x86_64-linux-gnu -emit-llvm -std=c++11 -o - -fcxx-exceptions -fexceptions | FileCheck -check-prefix=CHECK -check-prefix=CHECK11 %s
|
||||
|
||||
typedef __typeof(sizeof(0)) size_t;
|
||||
|
||||
@ -64,7 +65,10 @@ namespace test1 {
|
||||
// CHECK-NEXT: [[T2:%.*]] = load i32, i32* [[T1]], align 4
|
||||
// CHECK-NEXT: invoke void @_ZN5test11AC1Ei([[A]]* [[CAST]], i32 [[T2]])
|
||||
// CHECK: store i1 false, i1* [[ACTIVE]]
|
||||
// CHECK-NEXT: invoke void @_ZN5test11BD1Ev([[B]]* [[T0]])
|
||||
|
||||
// CHECK98-NEXT: invoke void @_ZN5test11BD1Ev([[B]]* [[T0]])
|
||||
// CHECK11-NEXT: call void @_ZN5test11BD1Ev([[B]]* [[T0]])
|
||||
|
||||
// CHECK: ret [[A]]* [[CAST]]
|
||||
// CHECK: [[ISACTIVE:%.*]] = load i1, i1* [[ACTIVE]]
|
||||
// CHECK-NEXT: br i1 [[ISACTIVE]]
|
||||
@ -74,10 +78,10 @@ namespace test1 {
|
||||
|
||||
// rdar://11904428
|
||||
// Terminate landing pads should call __cxa_begin_catch first.
|
||||
// CHECK: define linkonce_odr hidden void @__clang_call_terminate(i8*) [[NI_NR_NUW:#[0-9]+]] comdat
|
||||
// CHECK-NEXT: [[T0:%.*]] = call i8* @__cxa_begin_catch(i8* %0) [[NUW:#[0-9]+]]
|
||||
// CHECK-NEXT: call void @_ZSt9terminatev() [[NR_NUW:#[0-9]+]]
|
||||
// CHECK-NEXT: unreachable
|
||||
// CHECK98: define linkonce_odr hidden void @__clang_call_terminate(i8*) [[NI_NR_NUW:#[0-9]+]] comdat
|
||||
// CHECK98-NEXT: [[T0:%.*]] = call i8* @__cxa_begin_catch(i8* %0) [[NUW:#[0-9]+]]
|
||||
// CHECK98-NEXT: call void @_ZSt9terminatev() [[NR_NUW:#[0-9]+]]
|
||||
// CHECK98-NEXT: unreachable
|
||||
|
||||
A *d() {
|
||||
// CHECK: define [[A:%.*]]* @_ZN5test11dEv()
|
||||
@ -89,7 +93,10 @@ namespace test1 {
|
||||
// CHECK: [[T1:%.*]] = invoke i32 @_ZN5test11BcviEv([[B]]* [[T0]])
|
||||
// CHECK: invoke void @_ZN5test11AC1Ei([[A]]* [[CAST]], i32 [[T1]])
|
||||
// CHECK: store i1 false, i1* [[ACTIVE]]
|
||||
// CHECK-NEXT: invoke void @_ZN5test11BD1Ev([[B]]* [[T0]])
|
||||
|
||||
// CHECK98-NEXT: invoke void @_ZN5test11BD1Ev([[B]]* [[T0]])
|
||||
// CHECK11-NEXT: call void @_ZN5test11BD1Ev([[B]]* [[T0]])
|
||||
|
||||
// CHECK: ret [[A]]* [[CAST]]
|
||||
// CHECK: [[ISACTIVE:%.*]] = load i1, i1* [[ACTIVE]]
|
||||
// CHECK-NEXT: br i1 [[ISACTIVE]]
|
||||
@ -109,8 +116,13 @@ namespace test1 {
|
||||
// CHECK: [[T3:%.*]] = invoke i32 @_ZN5test11BcviEv([[B]]* [[T2]])
|
||||
// CHECK: invoke void @_ZN5test11AC1Eii([[A]]* [[CAST]], i32 [[T1]], i32 [[T3]])
|
||||
// CHECK: store i1 false, i1* [[ACTIVE]]
|
||||
// CHECK-NEXT: invoke void @_ZN5test11BD1Ev([[B]]* [[T2]])
|
||||
// CHECK: invoke void @_ZN5test11BD1Ev([[B]]* [[T0]])
|
||||
|
||||
// CHECK98-NEXT: invoke void @_ZN5test11BD1Ev([[B]]* [[T2]])
|
||||
// CHECK11-NEXT: call void @_ZN5test11BD1Ev([[B]]* [[T2]])
|
||||
|
||||
// CHECK98: invoke void @_ZN5test11BD1Ev([[B]]* [[T0]])
|
||||
// CHECK11: call void @_ZN5test11BD1Ev([[B]]* [[T0]])
|
||||
|
||||
// CHECK: ret [[A]]* [[CAST]]
|
||||
// CHECK: [[ISACTIVE:%.*]] = load i1, i1* [[ACTIVE]]
|
||||
// CHECK-NEXT: br i1 [[ISACTIVE]]
|
||||
@ -141,8 +153,13 @@ namespace test1 {
|
||||
// CHECK-NEXT: store [[A]]* [[CAST]], [[A]]** [[X]], align 8
|
||||
// CHECK: invoke void @_ZN5test15makeBEv([[B:%.*]]* sret [[T2:%.*]])
|
||||
// CHECK: [[RET:%.*]] = load [[A]]*, [[A]]** [[X]], align 8
|
||||
// CHECK: invoke void @_ZN5test11BD1Ev([[B]]* [[T2]])
|
||||
// CHECK: invoke void @_ZN5test11BD1Ev([[B]]* [[T0]])
|
||||
|
||||
// CHECK98: invoke void @_ZN5test11BD1Ev([[B]]* [[T2]])
|
||||
// CHECK11: call void @_ZN5test11BD1Ev([[B]]* [[T2]])
|
||||
|
||||
// CHECK98: invoke void @_ZN5test11BD1Ev([[B]]* [[T0]])
|
||||
// CHECK11: call void @_ZN5test11BD1Ev([[B]]* [[T0]])
|
||||
|
||||
// CHECK: ret [[A]]* [[RET]]
|
||||
// CHECK: [[ISACTIVE:%.*]] = load i1, i1* [[ACTIVE]]
|
||||
// CHECK-NEXT: br i1 [[ISACTIVE]]
|
||||
@ -166,8 +183,11 @@ namespace test2 {
|
||||
// CHECK-NEXT: [[CAST:%.*]] = bitcast i8* [[NEW]] to [[A]]*
|
||||
// CHECK-NEXT: invoke void @_ZN5test21AC1Ei([[A]]* [[CAST]], i32 5)
|
||||
// CHECK: ret [[A]]* [[CAST]]
|
||||
// CHECK: invoke void @_ZN5test21AdlEPvm(i8* [[NEW]], i64 8)
|
||||
// CHECK: call void @__clang_call_terminate(i8* {{%.*}}) [[NR_NUW]]
|
||||
|
||||
// CHECK98: invoke void @_ZN5test21AdlEPvm(i8* [[NEW]], i64 8)
|
||||
// CHECK11: call void @_ZN5test21AdlEPvm(i8* [[NEW]], i64 8)
|
||||
|
||||
// CHECK98: call void @__clang_call_terminate(i8* {{%.*}}) [[NR_NUW]]
|
||||
return new A(5);
|
||||
}
|
||||
}
|
||||
@ -192,8 +212,11 @@ namespace test3 {
|
||||
// CHECK-NEXT: [[CAST:%.*]] = bitcast i8* [[NEW]] to [[A]]*
|
||||
// CHECK-NEXT: invoke void @_ZN5test31AC1Ei([[A]]* [[CAST]], i32 5)
|
||||
// CHECK: ret [[A]]* [[CAST]]
|
||||
// CHECK: invoke void @_ZN5test31AdlEPvS1_d(i8* [[NEW]], i8* [[FOO]], double [[BAR]])
|
||||
// CHECK: call void @__clang_call_terminate(i8* {{%.*}}) [[NR_NUW]]
|
||||
|
||||
// CHECK98: invoke void @_ZN5test31AdlEPvS1_d(i8* [[NEW]], i8* [[FOO]], double [[BAR]])
|
||||
// CHECK11: call void @_ZN5test31AdlEPvS1_d(i8* [[NEW]], i8* [[FOO]], double [[BAR]])
|
||||
|
||||
// CHECK98: call void @__clang_call_terminate(i8* {{%.*}}) [[NR_NUW]]
|
||||
return new(foo(),bar()) A(5);
|
||||
}
|
||||
|
||||
@ -235,7 +258,9 @@ namespace test3 {
|
||||
// CHECK-NEXT: br i1 [[ISACTIVE]]
|
||||
// CHECK: [[V0:%.*]] = load i8*, i8** [[SAVED0]]
|
||||
// CHECK-NEXT: [[V1:%.*]] = load i8*, i8** [[SAVED1]]
|
||||
// CHECK-NEXT: invoke void @_ZN5test31AdlEPvS1_d(i8* [[V0]], i8* [[V1]], double [[CONST]])
|
||||
|
||||
// CHECK98-NEXT: invoke void @_ZN5test31AdlEPvS1_d(i8* [[V0]], i8* [[V1]], double [[CONST]])
|
||||
// CHECK11-NEXT: call void @_ZN5test31AdlEPvS1_d(i8* [[V0]], i8* [[V1]], double [[CONST]])
|
||||
}
|
||||
}
|
||||
|
||||
@ -283,9 +308,13 @@ namespace test5 {
|
||||
// CHECK-NEXT: [[SRC:%.*]] = bitcast i8* [[ADJ]] to [[A_T]]*
|
||||
// CHECK-NEXT: invoke void @_ZN5test51TC1Ev([[T_T]]* [[T]])
|
||||
// CHECK: invoke void @_ZN5test51AC1ERKS0_RKNS_1TE([[A_T]]* [[A]], [[A_T]]* dereferenceable({{[0-9]+}}) [[SRC]], [[T_T]]* dereferenceable({{[0-9]+}}) [[T]])
|
||||
// CHECK: invoke void @_ZN5test51TD1Ev([[T_T]]* [[T]])
|
||||
// CHECK: call i8* @__cxa_begin_catch(i8* [[EXN]]) [[NUW]]
|
||||
// CHECK-NEXT: invoke void @_ZN5test51AD1Ev([[A_T]]* [[A]])
|
||||
|
||||
// CHECK98: invoke void @_ZN5test51TD1Ev([[T_T]]* [[T]])
|
||||
// CHECK11: call void @_ZN5test51TD1Ev([[T_T]]* [[T]])
|
||||
|
||||
// CHECK98: call i8* @__cxa_begin_catch(i8* [[EXN]]) [[NUW]]
|
||||
// CHECK98-NEXT: invoke void @_ZN5test51AD1Ev([[A_T]]* [[A]])
|
||||
|
||||
// CHECK: call void @__cxa_end_catch()
|
||||
void test() {
|
||||
try {
|
||||
@ -380,12 +409,16 @@ namespace test7 {
|
||||
// Destroy the inner A object.
|
||||
// CHECK-NEXT: load i1, i1* [[INNER_A]]
|
||||
// CHECK-NEXT: br i1
|
||||
// CHECK: invoke void @_ZN5test71AD1Ev(
|
||||
|
||||
// CHECK98: invoke void @_ZN5test71AD1Ev(
|
||||
// CHECK11: call void @_ZN5test71AD1Ev(
|
||||
|
||||
// Destroy the outer A object.
|
||||
// CHECK: load i1, i1* [[OUTER_A]]
|
||||
// CHECK-NEXT: br i1
|
||||
// CHECK: invoke void @_ZN5test71AD1Ev(
|
||||
|
||||
// CHECK98: invoke void @_ZN5test71AD1Ev(
|
||||
// CHECK11: call void @_ZN5test71AD1Ev(
|
||||
|
||||
return new B(A(), new B(A(), 0));
|
||||
}
|
||||
@ -456,8 +489,12 @@ namespace test10 {
|
||||
// CHECK-NEXT: load i8, i8* @_ZN6test108suppressE, align 1
|
||||
// CHECK-NEXT: trunc
|
||||
// CHECK-NEXT: br i1
|
||||
// CHECK: call void @__cxa_end_catch()
|
||||
// CHECK-NEXT: br label
|
||||
|
||||
// CHECK98: call void @__cxa_end_catch()
|
||||
// CHECK98-NEXT: br label
|
||||
// CHECK11: invoke void @__cxa_end_catch()
|
||||
// CHECK11-NEXT: to label
|
||||
|
||||
// CHECK: invoke void @__cxa_rethrow()
|
||||
// CHECK: unreachable
|
||||
}
|
||||
@ -504,7 +541,10 @@ namespace test11 {
|
||||
// CHECK-NEXT: br i1 [[EMPTY]]
|
||||
// CHECK: [[AFTER:%.*]] = phi [[A]]* [ [[CUR]], {{%.*}} ], [ [[ELT:%.*]], {{%.*}} ]
|
||||
// CHECK-NEXT: [[ELT]] = getelementptr inbounds [[A]], [[A]]* [[AFTER]], i64 -1
|
||||
// CHECK-NEXT: invoke void @_ZN6test111AD1Ev([[A]]* [[ELT]])
|
||||
|
||||
// CHECK98-NEXT: invoke void @_ZN6test111AD1Ev([[A]]* [[ELT]])
|
||||
// CHECK11-NEXT: call void @_ZN6test111AD1Ev([[A]]* [[ELT]])
|
||||
|
||||
// CHECK: [[DONE:%.*]] = icmp eq [[A]]* [[ELT]], [[ARRAYBEGIN]]
|
||||
// CHECK-NEXT: br i1 [[DONE]],
|
||||
// - Next, chain to cleanup for single.
|
||||
@ -517,13 +557,19 @@ namespace test11 {
|
||||
// CHECK-NEXT: br label
|
||||
// CHECK: [[AFTER:%.*]] = phi [[A]]* [ [[ARRAYEND]], {{%.*}} ], [ [[ELT:%.*]], {{%.*}} ]
|
||||
// CHECK-NEXT: [[ELT]] = getelementptr inbounds [[A]], [[A]]* [[AFTER]], i64 -1
|
||||
// CHECK-NEXT: invoke void @_ZN6test111AD1Ev([[A]]* [[ELT]])
|
||||
|
||||
// CHECK98-NEXT: invoke void @_ZN6test111AD1Ev([[A]]* [[ELT]])
|
||||
// CHECK11-NEXT: call void @_ZN6test111AD1Ev([[A]]* [[ELT]])
|
||||
|
||||
// CHECK: [[DONE:%.*]] = icmp eq [[A]]* [[ELT]], [[ARRAYBEGIN]]
|
||||
// CHECK-NEXT: br i1 [[DONE]],
|
||||
// - Next, chain to cleanup for single.
|
||||
// CHECK: br label
|
||||
// Finally, the cleanup for single.
|
||||
// CHECK: invoke void @_ZN6test111AD1Ev([[A]]* [[SINGLE]])
|
||||
|
||||
// CHECK98: invoke void @_ZN6test111AD1Ev([[A]]* [[SINGLE]])
|
||||
// CHECK11: call void @_ZN6test111AD1Ev([[A]]* [[SINGLE]])
|
||||
|
||||
// CHECK: br label
|
||||
// CHECK: resume
|
||||
// (After this is a terminate landingpad.)
|
||||
@ -543,7 +589,9 @@ namespace test12 {
|
||||
// CHECK-NEXT: [[CAST:%.*]] = bitcast i8* [[PTR]] to [[A:%.*]]*
|
||||
// CHECK-NEXT: invoke void @_ZN6test121AC1Ev([[A]]* [[CAST]])
|
||||
// CHECK: ret [[A]]* [[CAST]]
|
||||
// CHECK: invoke void @_ZN6test121AdlEPvS1_(i8* [[PTR]], i8* [[PTR]])
|
||||
|
||||
// CHECK98: invoke void @_ZN6test121AdlEPvS1_(i8* [[PTR]], i8* [[PTR]])
|
||||
// CHECK11: call void @_ZN6test121AdlEPvS1_(i8* [[PTR]], i8* [[PTR]])
|
||||
}
|
||||
|
||||
// CHECK: attributes [[NI_NR_NUW]] = { noinline noreturn nounwind }
|
||||
// CHECK98: attributes [[NI_NR_NUW]] = { noinline noreturn nounwind }
|
||||
|
@ -1,4 +1,5 @@
|
||||
// RUN: %clang_cc1 %s -triple=x86_64-apple-darwin10 -fcxx-exceptions -fexceptions -emit-llvm -o - | FileCheck %s
|
||||
// RUN: %clang_cc1 %s -triple=x86_64-apple-darwin10 -fcxx-exceptions -fexceptions -emit-llvm -std=c++98 -o - | FileCheck -check-prefix=CHECK -check-prefix=CHECK98 %s
|
||||
// RUN: %clang_cc1 %s -triple=x86_64-apple-darwin10 -fcxx-exceptions -fexceptions -emit-llvm -std=c++11 -o - | FileCheck -check-prefix=CHECK -check-prefix=CHECK11 %s
|
||||
|
||||
// Reduced from a crash on boost::interprocess's node_allocator_test.cpp.
|
||||
namespace test0 {
|
||||
@ -24,7 +25,9 @@ namespace test0 {
|
||||
// CHECK-NEXT: invoke void @_ZN5test01AC1Ev([[A]]* [[TMP]])
|
||||
// CHECK: invoke void @_ZN5test01VC1ERKNS_1AE([[V]]* [[NEWCAST]], [[A]]* dereferenceable({{[0-9]+}}) [[TMP]])
|
||||
// CHECK: store i1 false, i1* [[CLEANUPACTIVE]]
|
||||
// CHECK-NEXT: invoke void @_ZN5test01AD1Ev([[A]]* [[TMP]])
|
||||
|
||||
// CHECK98-NEXT: invoke void @_ZN5test01AD1Ev([[A]]* [[TMP]])
|
||||
// CHECK11-NEXT: call void @_ZN5test01AD1Ev([[A]]* [[TMP]])
|
||||
A y;
|
||||
try {
|
||||
A z;
|
||||
|
@ -1,22 +1,22 @@
|
||||
// Check -B driver option.
|
||||
//
|
||||
// RUN: %clang %s -### -o %t.o -target i386-unknown-linux \
|
||||
// RUN: -B %S/Inputs/B_opt_tree/dir1 2>&1 \
|
||||
// RUN: -B %S/Inputs/B_opt_tree/dir1 -fuse-ld=ld 2>&1 \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-B-OPT-TRIPLE %s
|
||||
// CHECK-B-OPT-TRIPLE: "{{.*}}/Inputs/B_opt_tree/dir1{{/|\\\\}}i386-unknown-linux-ld"
|
||||
//
|
||||
// RUN: %clang %s -### -o %t.o -target i386-unknown-linux \
|
||||
// RUN: -B %S/Inputs/B_opt_tree/dir2 2>&1 \
|
||||
// RUN: -B %S/Inputs/B_opt_tree/dir2 -fuse-ld=ld 2>&1 \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-B-OPT-DIR %s
|
||||
// CHECK-B-OPT-DIR: "{{.*}}/Inputs/B_opt_tree/dir2{{/|\\\\}}ld"
|
||||
//
|
||||
// RUN: %clang %s -### -o %t.o -target i386-unknown-linux \
|
||||
// RUN: -B %S/Inputs/B_opt_tree/dir3/prefix- 2>&1 \
|
||||
// RUN: -B %S/Inputs/B_opt_tree/dir3/prefix- -fuse-ld=ld 2>&1 \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-B-OPT-PREFIX %s
|
||||
// CHECK-B-OPT-PREFIX: "{{.*}}/Inputs/B_opt_tree/dir3{{/|\\\\}}prefix-ld"
|
||||
//
|
||||
// RUN: %clang %s -### -o %t.o -target i386-unknown-linux \
|
||||
// RUN: -B %S/Inputs/B_opt_tree/dir3/prefix- \
|
||||
// RUN: -B %S/Inputs/B_opt_tree/dir2 2>&1 \
|
||||
// RUN: -B %S/Inputs/B_opt_tree/dir2 2>&1 -fuse-ld=ld \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-B-OPT-MULT %s
|
||||
// CHECK-B-OPT-MULT: "{{.*}}/Inputs/B_opt_tree/dir3{{/|\\\\}}prefix-ld"
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Test coverage ld flags.
|
||||
//
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target i386-unknown-linux --coverage \
|
||||
// RUN: -target i386-unknown-linux --coverage -fuse-ld=ld \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-LINUX-I386 %s
|
||||
@ -10,7 +10,7 @@
|
||||
// CHECK-LINUX-I386: "{{.*}}/Inputs/resource_dir{{/|\\\\}}lib{{/|\\\\}}linux{{/|\\\\}}libclang_rt.profile-i386.a" {{.*}} "-lc"
|
||||
//
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-unknown-linux --coverage \
|
||||
// RUN: -target x86_64-unknown-linux --coverage -fuse-ld=ld \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-LINUX-X86-64 %s
|
||||
@ -19,7 +19,7 @@
|
||||
// CHECK-LINUX-X86-64: "{{.*}}/Inputs/resource_dir{{/|\\\\}}lib{{/|\\\\}}linux{{/|\\\\}}libclang_rt.profile-x86_64.a" {{.*}} "-lc"
|
||||
//
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-unknown-freebsd --coverage \
|
||||
// RUN: -target x86_64-unknown-freebsd --coverage -fuse-ld=ld \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir \
|
||||
// RUN: --sysroot=%S/Inputs/basic_freebsd64_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-FREEBSD-X86-64 %s
|
||||
@ -28,7 +28,7 @@
|
||||
// CHECK-FREEBSD-X86-64: "{{.*}}/Inputs/resource_dir{{/|\\\\}}lib{{/|\\\\}}freebsd{{/|\\\\}}libclang_rt.profile-x86_64.a"
|
||||
//
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target arm-linux-androideabi --coverage \
|
||||
// RUN: -target arm-linux-androideabi --coverage -fuse-ld=ld \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir \
|
||||
// RUN: --sysroot=%S/Inputs/basic_android_tree/sysroot \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-ANDROID-ARM %s
|
||||
|
@ -1,4 +1,4 @@
|
||||
// RUN: %clang -### -o %t %s 2>&1 -no-integrated-as \
|
||||
// RUN: %clang -### -o %t %s 2>&1 -no-integrated-as -fuse-ld=ld \
|
||||
// RUN: --gcc-toolchain=%S/Inputs/basic_cross_linux_tree/usr \
|
||||
// RUN: --target=i386-unknown-linux-gnu \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-I386 %s
|
||||
@ -6,7 +6,7 @@
|
||||
// CHECK-I386: "{{.*}}/Inputs/basic_cross_linux_tree/usr/lib/gcc/i386-unknown-linux-gnu/4.6.0/../../../../i386-unknown-linux-gnu/bin{{/|\\\\}}as" "--32"
|
||||
// CHECK-I386: "{{.*}}/Inputs/basic_cross_linux_tree/usr/lib/gcc/i386-unknown-linux-gnu/4.6.0/../../../../i386-unknown-linux-gnu/bin{{/|\\\\}}ld" {{.*}} "-m" "elf_i386"
|
||||
//
|
||||
// RUN: %clang -### -o %t %s 2>&1 -no-integrated-as \
|
||||
// RUN: %clang -### -o %t %s 2>&1 -no-integrated-as -fuse-ld=ld \
|
||||
// RUN: --gcc-toolchain=%S/Inputs/basic_cross_linux_tree/usr \
|
||||
// RUN: --target=x86_64-unknown-linux-gnu \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-X86-64 %s
|
||||
@ -14,7 +14,7 @@
|
||||
// CHECK-X86-64: "{{.*}}/Inputs/basic_cross_linux_tree/usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.0/../../../../x86_64-unknown-linux-gnu/bin{{/|\\\\}}as" "--64"
|
||||
// CHECK-X86-64: "{{.*}}/Inputs/basic_cross_linux_tree/usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.0/../../../../x86_64-unknown-linux-gnu/bin{{/|\\\\}}ld" {{.*}} "-m" "elf_x86_64"
|
||||
//
|
||||
// RUN: %clang -### -o %t %s 2>&1 -no-integrated-as \
|
||||
// RUN: %clang -### -o %t %s 2>&1 -no-integrated-as -fuse-ld=ld \
|
||||
// RUN: --gcc-toolchain=%S/Inputs/basic_cross_linux_tree/usr \
|
||||
// RUN: --target=x86_64-unknown-linux-gnux32 \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-X32 %s
|
||||
@ -22,17 +22,17 @@
|
||||
// CHECK-X32: "{{.*}}/Inputs/basic_cross_linux_tree/usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.0/../../../../x86_64-unknown-linux-gnu/bin{{/|\\\\}}as" "--x32"
|
||||
// CHECK-X32: "{{.*}}/Inputs/basic_cross_linux_tree/usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.0/../../../../x86_64-unknown-linux-gnu/bin{{/|\\\\}}ld" {{.*}} "-m" "elf32_x86_64"
|
||||
//
|
||||
// RUN: %clang -### -o %t %s 2>&1 -no-integrated-as \
|
||||
// RUN: %clang -### -o %t %s 2>&1 -no-integrated-as -fuse-ld=ld \
|
||||
// RUN: --gcc-toolchain=%S/Inputs/basic_cross_linux_tree/usr \
|
||||
// RUN: --target=x86_64-unknown-linux-gnu -m32 \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-I386 %s
|
||||
//
|
||||
// RUN: %clang -### -o %t %s 2>&1 -no-integrated-as \
|
||||
// RUN: %clang -### -o %t %s 2>&1 -no-integrated-as -fuse-ld=ld \
|
||||
// RUN: --gcc-toolchain=%S/Inputs/basic_cross_linux_tree/usr \
|
||||
// RUN: --target=i386-unknown-linux-gnu -m64 \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-X86-64 %s
|
||||
//
|
||||
// RUN: %clang -### -o %t %s 2>&1 -no-integrated-as \
|
||||
// RUN: %clang -### -o %t %s 2>&1 -no-integrated-as -fuse-ld=ld \
|
||||
// RUN: --gcc-toolchain=%S/Inputs/multilib_32bit_linux_tree/usr \
|
||||
// RUN: --target=i386-unknown-linux \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
@ -49,7 +49,7 @@
|
||||
// CHECK-MULTI32-I386: "-L[[sysroot]]/lib"
|
||||
// CHECK-MULTI32-I386: "-L[[sysroot]]/usr/lib"
|
||||
//
|
||||
// RUN: %clang -### -o %t %s 2>&1 -no-integrated-as \
|
||||
// RUN: %clang -### -o %t %s 2>&1 -no-integrated-as -fuse-ld=ld \
|
||||
// RUN: --gcc-toolchain=%S/Inputs/multilib_32bit_linux_tree/usr \
|
||||
// RUN: --target=x86_64-unknown-linux \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
@ -67,7 +67,7 @@
|
||||
// CHECK-MULTI32-X86-64: "-L[[sysroot]]/lib"
|
||||
// CHECK-MULTI32-X86-64: "-L[[sysroot]]/usr/lib"
|
||||
//
|
||||
// RUN: %clang -### -o %t %s 2>&1 -no-integrated-as \
|
||||
// RUN: %clang -### -o %t %s 2>&1 -no-integrated-as -fuse-ld=ld \
|
||||
// RUN: --gcc-toolchain=%S/Inputs/multilib_64bit_linux_tree/usr \
|
||||
// RUN: --target=i386-unknown-linux \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
@ -85,7 +85,7 @@
|
||||
// CHECK-MULTI64-I386: "-L[[sysroot]]/lib"
|
||||
// CHECK-MULTI64-I386: "-L[[sysroot]]/usr/lib"
|
||||
//
|
||||
// RUN: %clang -### -o %t %s 2>&1 -no-integrated-as \
|
||||
// RUN: %clang -### -o %t %s 2>&1 -no-integrated-as -fuse-ld=ld \
|
||||
// RUN: --gcc-toolchain=%S/Inputs/multilib_64bit_linux_tree/usr \
|
||||
// RUN: --target=x86_64-unknown-linux \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
|
@ -1,5 +1,5 @@
|
||||
// RUN: %clang %s -### -no-canonical-prefixes --target=x86_64-unknown-fuchsia \
|
||||
// RUN: --sysroot=%S/platform 2>&1 | FileCheck %s
|
||||
// RUN: --sysroot=%S/platform -fuse-ld=ld 2>&1 | FileCheck %s
|
||||
// CHECK: {{.*}}clang{{.*}}" "-cc1"
|
||||
// CHECK: "-fuse-init-array"
|
||||
// CHECK: "-isysroot" "[[SYSROOT:[^"]+]]"
|
||||
|
@ -1,5 +1,5 @@
|
||||
// RUN: %clangxx %s -### -no-canonical-prefixes --target=x86_64-unknown-fuchsia \
|
||||
// RUN: --sysroot=%S/platform 2>&1 | FileCheck %s
|
||||
// RUN: --sysroot=%S/platform 2>&1 -fuse-ld=ld | FileCheck %s
|
||||
// CHECK: {{.*}}clang{{.*}}" "-cc1"
|
||||
// CHECK: "-fuse-init-array"
|
||||
// CHECK: "-isysroot" "[[SYSROOT:[^"]+]]"
|
||||
|
@ -32,7 +32,7 @@
|
||||
|
||||
|
||||
|
||||
// RUN: %clang %s -### \
|
||||
// RUN: %clang %s -### -fuse-ld=ld \
|
||||
// RUN: -target arm-linux-androideabi \
|
||||
// RUN: -B%S/Inputs/basic_android_tree/bin 2>&1 \
|
||||
// RUN: | FileCheck %s --check-prefix=CHECK-ANDROID-ARM-LD
|
||||
@ -50,7 +50,7 @@
|
||||
// RUN: | FileCheck %s -check-prefix=CHECK-ANDROID-ARM-GOLD
|
||||
// CHECK-ANDROID-ARM-GOLD: Inputs/basic_android_tree/bin{{/|\\+}}arm-linux-androideabi-ld.gold
|
||||
|
||||
// RUN: %clang %s -### \
|
||||
// RUN: %clang %s -### -fuse-ld=ld \
|
||||
// RUN: -target arm-linux-androideabi \
|
||||
// RUN: -gcc-toolchain %S/Inputs/basic_android_tree 2>&1 \
|
||||
// RUN: | FileCheck %s --check-prefix=CHECK-ANDROID-ARM-LD-TC
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Test instrumented profiling ld flags.
|
||||
//
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target i386-unknown-linux -fprofile-instr-generate \
|
||||
// RUN: -target i386-unknown-linux -fprofile-instr-generate -fuse-ld=ld \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-LINUX-I386 %s
|
||||
@ -10,7 +10,7 @@
|
||||
// CHECK-LINUX-I386: "{{.*}}/Inputs/resource_dir{{/|\\\\}}lib{{/|\\\\}}linux{{/|\\\\}}libclang_rt.profile-i386.a" {{.*}} "-lc"
|
||||
//
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-unknown-linux -fprofile-instr-generate \
|
||||
// RUN: -target x86_64-unknown-linux -fprofile-instr-generate -fuse-ld=ld \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-LINUX-X86-64 %s
|
||||
@ -19,7 +19,7 @@
|
||||
// CHECK-LINUX-X86-64: "{{.*}}/Inputs/resource_dir{{/|\\\\}}lib{{/|\\\\}}linux{{/|\\\\}}libclang_rt.profile-x86_64.a" {{.*}} "-lc"
|
||||
//
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-unknown-linux -fprofile-instr-generate -nostdlib \
|
||||
// RUN: -target x86_64-unknown-linux -fprofile-instr-generate -nostdlib -fuse-ld=ld \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-LINUX-NOSTDLIB-X86-64 %s
|
||||
@ -28,7 +28,7 @@
|
||||
// CHECK-LINUX-NOSTDLIB-X86-64: "{{.*}}/Inputs/resource_dir{{/|\\\\}}lib{{/|\\\\}}linux{{/|\\\\}}libclang_rt.profile-x86_64.a"
|
||||
//
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-unknown-freebsd -fprofile-instr-generate \
|
||||
// RUN: -target x86_64-unknown-freebsd -fprofile-instr-generate -fuse-ld=ld \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir \
|
||||
// RUN: --sysroot=%S/Inputs/basic_freebsd64_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-FREEBSD-X86-64 %s
|
||||
@ -38,7 +38,7 @@
|
||||
//
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -shared \
|
||||
// RUN: -target i386-unknown-linux -fprofile-instr-generate \
|
||||
// RUN: -target i386-unknown-linux -fprofile-instr-generate -fuse-ld=ld \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-LINUX-I386-SHARED %s
|
||||
@ -48,7 +48,7 @@
|
||||
//
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -shared \
|
||||
// RUN: -target x86_64-unknown-linux -fprofile-instr-generate \
|
||||
// RUN: -target x86_64-unknown-linux -fprofile-instr-generate -fuse-ld=ld \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-LINUX-X86-64-SHARED %s
|
||||
@ -58,7 +58,7 @@
|
||||
//
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -shared \
|
||||
// RUN: -target x86_64-unknown-freebsd -fprofile-instr-generate \
|
||||
// RUN: -target x86_64-unknown-freebsd -fprofile-instr-generate -fuse-ld=ld \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir \
|
||||
// RUN: --sysroot=%S/Inputs/basic_freebsd64_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-FREEBSD-X86-64-SHARED %s
|
||||
@ -67,7 +67,7 @@
|
||||
// CHECK-FREEBSD-X86-64-SHARED: "{{.*}}/Inputs/resource_dir{{/|\\\\}}lib{{/|\\\\}}freebsd{{/|\\\\}}libclang_rt.profile-x86_64.a"
|
||||
//
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-apple-darwin14 -fprofile-instr-generate \
|
||||
// RUN: -target x86_64-apple-darwin14 -fprofile-instr-generate -fuse-ld=ld \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-DARWIN-X86-64 %s
|
||||
//
|
||||
@ -75,7 +75,7 @@
|
||||
// CHECK-DARWIN-X86-64: "{{.*}}/Inputs/resource_dir{{/|\\\\}}lib{{/|\\\\}}darwin{{/|\\\\}}libclang_rt.profile_osx.a"
|
||||
//
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-apple-darwin14 -fprofile-instr-generate -nostdlib \
|
||||
// RUN: -target x86_64-apple-darwin14 -fprofile-instr-generate -nostdlib -fuse-ld=ld \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-DARWIN-NOSTDLIB-X86-64 %s
|
||||
//
|
||||
@ -83,7 +83,7 @@
|
||||
// CHECK-DARWIN-NOSTDLIB-X86-64: "{{.*}}/Inputs/resource_dir{{/|\\\\}}lib{{/|\\\\}}darwin{{/|\\\\}}libclang_rt.profile_osx.a"
|
||||
//
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target arm64-apple-ios -fprofile-instr-generate \
|
||||
// RUN: -target arm64-apple-ios -fprofile-instr-generate -fuse-ld=ld \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-DARWIN-ARM64 %s
|
||||
//
|
||||
@ -91,7 +91,7 @@
|
||||
// CHECK-DARWIN-ARM64: "{{.*}}/Inputs/resource_dir{{/|\\\\}}lib{{/|\\\\}}darwin{{/|\\\\}}libclang_rt.profile_ios.a"
|
||||
//
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target armv7-apple-darwin -mtvos-version-min=8.3 -fprofile-instr-generate \
|
||||
// RUN: -target armv7-apple-darwin -mtvos-version-min=8.3 -fprofile-instr-generate -fuse-ld=ld \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-TVOS-ARMV7 %s
|
||||
//
|
||||
@ -99,7 +99,7 @@
|
||||
// CHECK-TVOS-ARMV7: "{{.*}}/Inputs/resource_dir{{/|\\\\}}lib{{/|\\\\}}darwin{{/|\\\\}}libclang_rt.profile_tvos.a"
|
||||
//
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target armv7s-apple-darwin10 -mwatchos-version-min=2.0 -arch armv7k -fprofile-instr-generate \
|
||||
// RUN: -target armv7s-apple-darwin10 -mwatchos-version-min=2.0 -arch armv7k -fprofile-instr-generate -fuse-ld=ld \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-WATCHOS-ARMV7 %s
|
||||
//
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
// = Big-endian, mips32r2, hard float
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: --target=mips-mti-linux -mips32r2 -mhard-float -rtlib=platform \
|
||||
// RUN: --target=mips-mti-linux -mips32r2 -mhard-float -rtlib=platform -fuse-ld=ld \
|
||||
// RUN: --sysroot=%S/Inputs/mips_mti_linux/sysroot \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-BE-HF-32R2 %s
|
||||
//
|
||||
@ -26,7 +26,7 @@
|
||||
|
||||
// = Little-endian, mips32r2, hard float
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: --target=mips-mti-linux -mips32r2 -EL -mhard-float -rtlib=platform \
|
||||
// RUN: --target=mips-mti-linux -mips32r2 -EL -mhard-float -rtlib=platform -fuse-ld=ld \
|
||||
// RUN: --sysroot=%S/Inputs/mips_mti_linux/sysroot \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-LE-HF-32R2 %s
|
||||
//
|
||||
|
@ -23,6 +23,12 @@
|
||||
// RUN: %clang -no-canonical-prefixes -target aarch64--netbsd7.0.0 \
|
||||
// RUN: --sysroot=%S/Inputs/basic_netbsd_tree %s -### 2>&1 \
|
||||
// RUN: | FileCheck -check-prefix=AARCH64-7 %s
|
||||
// RUN: %clang -no-canonical-prefixes -target aarch64_be--netbsd \
|
||||
// RUN: --sysroot=%S/Inputs/basic_netbsd_tree %s -### 2>&1 \
|
||||
// RUN: | FileCheck -check-prefix=AARCH64_BE %s
|
||||
// RUN: %clang -no-canonical-prefixes -target aarch64_be--netbsd7.0.0 \
|
||||
// RUN: --sysroot=%S/Inputs/basic_netbsd_tree %s -### 2>&1 \
|
||||
// RUN: | FileCheck -check-prefix=AARCH64_BE-7 %s
|
||||
// RUN: %clang -no-canonical-prefixes -target arm--netbsd-eabi \
|
||||
// RUN: -no-integrated-as --sysroot=%S/Inputs/basic_netbsd_tree %s -### 2>&1 \
|
||||
// RUN: | FileCheck -check-prefix=ARM %s
|
||||
@ -84,6 +90,12 @@
|
||||
// RUN: %clang -no-canonical-prefixes -target aarch64--netbsd7.0.0 -static \
|
||||
// RUN: --sysroot=%S/Inputs/basic_netbsd_tree %s -### 2>&1 \
|
||||
// RUN: | FileCheck -check-prefix=S-AARCH64-7 %s
|
||||
// RUN: %clang -no-canonical-prefixes -target aarch64_be--netbsd -static \
|
||||
// RUN: --sysroot=%S/Inputs/basic_netbsd_tree %s -### 2>&1 \
|
||||
// RUN: | FileCheck -check-prefix=S-AARCH64_BE %s
|
||||
// RUN: %clang -no-canonical-prefixes -target aarch64_be--netbsd7.0.0 -static \
|
||||
// RUN: --sysroot=%S/Inputs/basic_netbsd_tree %s -### 2>&1 \
|
||||
// RUN: | FileCheck -check-prefix=S-AARCH64_BE-7 %s
|
||||
// RUN: %clang -no-canonical-prefixes -target arm--netbsd-eabi -static \
|
||||
// RUN: --sysroot=%S/Inputs/basic_netbsd_tree %s -### 2>&1 \
|
||||
// RUN: | FileCheck -check-prefix=S-ARM %s
|
||||
@ -171,6 +183,18 @@
|
||||
// AARCH64-7: "{{.*}}/usr/lib{{/|\\\\}}crtbegin.o" "{{.*}}.o" "-lc"
|
||||
// AARCH64-7: "{{.*}}/usr/lib{{/|\\\\}}crtend.o" "{{.*}}/usr/lib{{/|\\\\}}crtn.o"
|
||||
|
||||
// AARCH64_BE: clang{{.*}}" "-cc1" "-triple" "aarch64_be--netbsd"
|
||||
// AARCH64_BE: ld{{.*}}" "--eh-frame-hdr" "-dynamic-linker" "/libexec/ld.elf_so"
|
||||
// AARCH64_BE: "-o" "a.out" "{{.*}}/usr/lib{{/|\\\\}}crt0.o" "{{.*}}/usr/lib{{/|\\\\}}crti.o"
|
||||
// AARCH64_BE: "{{.*}}/usr/lib{{/|\\\\}}crtbegin.o" "{{.*}}.o" "-lc"
|
||||
// AARCH64_BE: "{{.*}}/usr/lib{{/|\\\\}}crtend.o" "{{.*}}/usr/lib{{/|\\\\}}crtn.o"
|
||||
|
||||
// AARCH64_BE-7: clang{{.*}}" "-cc1" "-triple" "aarch64_be--netbsd7.0.0"
|
||||
// AARCH64_BE-7: ld{{.*}}" "--eh-frame-hdr" "-dynamic-linker" "/libexec/ld.elf_so"
|
||||
// AARCH64_BE-7: "-o" "a.out" "{{.*}}/usr/lib{{/|\\\\}}crt0.o" "{{.*}}/usr/lib{{/|\\\\}}crti.o"
|
||||
// AARCH64_BE-7: "{{.*}}/usr/lib{{/|\\\\}}crtbegin.o" "{{.*}}.o" "-lc"
|
||||
// AARCH64_BE-7: "{{.*}}/usr/lib{{/|\\\\}}crtend.o" "{{.*}}/usr/lib{{/|\\\\}}crtn.o"
|
||||
|
||||
// ARM: clang{{.*}}" "-cc1" "-triple" "armv5e--netbsd-eabi"
|
||||
// ARM: as{{.*}}" "-mcpu=arm926ej-s" "-o"
|
||||
// ARM: ld{{.*}}" "--eh-frame-hdr" "-dynamic-linker" "/libexec/ld.elf_so"
|
||||
@ -311,6 +335,18 @@
|
||||
// S-AARCH64-7: "{{.*}}/usr/lib{{/|\\\\}}crtbegin.o" "{{.*}}.o" "-lc"
|
||||
// S-AARCH64-7: "{{.*}}/usr/lib{{/|\\\\}}crtend.o" "{{.*}}/usr/lib{{/|\\\\}}crtn.o"
|
||||
|
||||
// S-AARCH64_BE: clang{{.*}}" "-cc1" "-triple" "aarch64_be--netbsd"
|
||||
// S-AARCH64_BE: ld{{.*}}" "--eh-frame-hdr" "-Bstatic"
|
||||
// S-AARCH64_BE: "-o" "a.out" "{{.*}}/usr/lib{{/|\\\\}}crt0.o" "{{.*}}/usr/lib{{/|\\\\}}crti.o"
|
||||
// S-AARCH64_BE: "{{.*}}/usr/lib{{/|\\\\}}crtbegin.o" "{{.*}}.o" "-lc"
|
||||
// S-AARCH64_BE: "{{.*}}/usr/lib{{/|\\\\}}crtend.o" "{{.*}}/usr/lib{{/|\\\\}}crtn.o"
|
||||
|
||||
// S-AARCH64_BE-7: clang{{.*}}" "-cc1" "-triple" "aarch64_be--netbsd7.0.0"
|
||||
// S-AARCH64_BE-7: ld{{.*}}" "--eh-frame-hdr" "-Bstatic"
|
||||
// S-AARCH64_BE-7: "-o" "a.out" "{{.*}}/usr/lib{{/|\\\\}}crt0.o" "{{.*}}/usr/lib{{/|\\\\}}crti.o"
|
||||
// S-AARCH64_BE-7: "{{.*}}/usr/lib{{/|\\\\}}crtbegin.o" "{{.*}}.o" "-lc"
|
||||
// S-AARCH64_BE-7: "{{.*}}/usr/lib{{/|\\\\}}crtend.o" "{{.*}}/usr/lib{{/|\\\\}}crtn.o"
|
||||
|
||||
// S-ARM: clang{{.*}}" "-cc1" "-triple" "armv5e--netbsd-eabi"
|
||||
// S-ARM: ld{{.*}}" "--eh-frame-hdr" "-Bstatic"
|
||||
// S-ARM: "-m" "armelf_nbsd_eabi"
|
||||
|
@ -19,6 +19,12 @@
|
||||
// RUN: %clangxx -no-canonical-prefixes -target aarch64--netbsd7.0.0 \
|
||||
// RUN: -stdlib=platform --sysroot=%S/Inputs/basic_netbsd_tree %s -### 2>&1 \
|
||||
// RUN: | FileCheck -check-prefix=AARCH64-7 %s
|
||||
// RUN: %clangxx -no-canonical-prefixes -target aarch64_be--netbsd \
|
||||
// RUN: -stdlib=platform --sysroot=%S/Inputs/basic_netbsd_tree %s -### 2>&1 \
|
||||
// RUN: | FileCheck -check-prefix=AARCH64_BE %s
|
||||
// RUN: %clangxx -no-canonical-prefixes -target aarch64_be--netbsd7.0.0 \
|
||||
// RUN: -stdlib=platform --sysroot=%S/Inputs/basic_netbsd_tree %s -### 2>&1 \
|
||||
// RUN: | FileCheck -check-prefix=AARCH64_BE-7 %s
|
||||
// RUN: %clangxx -no-canonical-prefixes -target sparc--netbsd \
|
||||
// RUN: -stdlib=platform --sysroot=%S/Inputs/basic_netbsd_tree %s -### 2>&1 \
|
||||
// RUN: | FileCheck -check-prefix=SPARC %s
|
||||
@ -65,6 +71,12 @@
|
||||
// RUN: %clangxx -no-canonical-prefixes -target aarch64--netbsd7.0.0 -static \
|
||||
// RUN: -stdlib=platform --sysroot=%S/Inputs/basic_netbsd_tree %s -### 2>&1 \
|
||||
// RUN: | FileCheck -check-prefix=S-AARCH64-7 %s
|
||||
// RUN: %clangxx -no-canonical-prefixes -target aarch64_be--netbsd -static \
|
||||
// RUN: -stdlib=platform --sysroot=%S/Inputs/basic_netbsd_tree %s -### 2>&1 \
|
||||
// RUN: | FileCheck -check-prefix=S-AARCH64_BE %s
|
||||
// RUN: %clangxx -no-canonical-prefixes -target aarch64_be--netbsd7.0.0 -static \
|
||||
// RUN: -stdlib=platform --sysroot=%S/Inputs/basic_netbsd_tree %s -### 2>&1 \
|
||||
// RUN: | FileCheck -check-prefix=S-AARCH64_BE-7 %s
|
||||
// RUN: %clangxx -no-canonical-prefixes -target sparc--netbsd -static \
|
||||
// RUN: -stdlib=platform --sysroot=%S/Inputs/basic_netbsd_tree %s -### 2>&1 \
|
||||
// RUN: | FileCheck -check-prefix=S-SPARC %s
|
||||
@ -136,6 +148,20 @@
|
||||
// AARCH64-7: "-lm" "-lc"
|
||||
// AARCH64-7: "{{.*}}/usr/lib{{/|\\\\}}crtend.o" "{{.*}}/usr/lib{{/|\\\\}}crtn.o"
|
||||
|
||||
// AARCH64_BE: clang{{.*}}" "-cc1" "-triple" "aarch64_be--netbsd"
|
||||
// AARCH64_BE: ld{{.*}}" "--eh-frame-hdr" "-dynamic-linker" "/libexec/ld.elf_so"
|
||||
// AARCH64_BE: "-o" "a.out" "{{.*}}/usr/lib{{/|\\\\}}crt0.o" "{{.*}}/usr/lib{{/|\\\\}}crti.o"
|
||||
// AARCH64_BE: "{{.*}}/usr/lib{{/|\\\\}}crtbegin.o" "{{.*}}.o" "-lc++"
|
||||
// AARCH64_BE: "-lm" "-lc"
|
||||
// AARCH64_BE: "{{.*}}/usr/lib{{/|\\\\}}crtend.o" "{{.*}}/usr/lib{{/|\\\\}}crtn.o"
|
||||
|
||||
// AARCH64_BE-7: clang{{.*}}" "-cc1" "-triple" "aarch64_be--netbsd7.0.0"
|
||||
// AARCH64_BE-7: ld{{.*}}" "--eh-frame-hdr" "-dynamic-linker" "/libexec/ld.elf_so"
|
||||
// AARCH64_BE-7: "-o" "a.out" "{{.*}}/usr/lib{{/|\\\\}}crt0.o" "{{.*}}/usr/lib{{/|\\\\}}crti.o"
|
||||
// AARCH64_BE-7: "{{.*}}/usr/lib{{/|\\\\}}crtbegin.o" "{{.*}}.o" "-lc++"
|
||||
// AARCH64_BE-7: "-lm" "-lc"
|
||||
// AARCH64_BE-7: "{{.*}}/usr/lib{{/|\\\\}}crtend.o" "{{.*}}/usr/lib{{/|\\\\}}crtn.o"
|
||||
|
||||
// SPARC: clang{{.*}}" "-cc1" "-triple" "sparc--netbsd"
|
||||
// SPARC: ld{{.*}}" "--eh-frame-hdr" "-dynamic-linker" "/libexec/ld.elf_so"
|
||||
// SPARC: "-o" "a.out" "{{.*}}/usr/lib{{/|\\\\}}crt0.o"
|
||||
@ -241,6 +267,20 @@
|
||||
// S-AARCH64-7: "-lm" "-lc"
|
||||
// S-AARCH64-7: "{{.*}}/usr/lib{{/|\\\\}}crtend.o" "{{.*}}/usr/lib{{/|\\\\}}crtn.o"
|
||||
|
||||
// S-AARCH64_BE: clang{{.*}}" "-cc1" "-triple" "aarch64_be--netbsd"
|
||||
// S-AARCH64_BE: ld{{.*}}" "--eh-frame-hdr" "-Bstatic"
|
||||
// S-AARCH64_BE: "-o" "a.out" "{{.*}}/usr/lib{{/|\\\\}}crt0.o" "{{.*}}/usr/lib{{/|\\\\}}crti.o"
|
||||
// S-AARCH64_BE: "{{.*}}/usr/lib{{/|\\\\}}crtbegin.o" "{{.*}}.o" "-lc++"
|
||||
// S-AARCH64_BE: "-lm" "-lc"
|
||||
// S-AARCH64_BE: "{{.*}}/usr/lib{{/|\\\\}}crtend.o" "{{.*}}/usr/lib{{/|\\\\}}crtn.o"
|
||||
|
||||
// S-AARCH64_BE-7: clang{{.*}}" "-cc1" "-triple" "aarch64_be--netbsd7.0.0"
|
||||
// S-AARCH64_BE-7: ld{{.*}}" "--eh-frame-hdr" "-Bstatic"
|
||||
// S-AARCH64_BE-7: "-o" "a.out" "{{.*}}/usr/lib{{/|\\\\}}crt0.o" "{{.*}}/usr/lib{{/|\\\\}}crti.o"
|
||||
// S-AARCH64_BE-7: "{{.*}}/usr/lib{{/|\\\\}}crtbegin.o" "{{.*}}.o" "-lc++"
|
||||
// S-AARCH64_BE-7: "-lm" "-lc"
|
||||
// S-AARCH64_BE-7: "{{.*}}/usr/lib{{/|\\\\}}crtend.o" "{{.*}}/usr/lib{{/|\\\\}}crtn.o"
|
||||
|
||||
// S-SPARC: clang{{.*}}" "-cc1" "-triple" "sparc--netbsd"
|
||||
// S-SPARC: ld{{.*}}" "--eh-frame-hdr" "-Bstatic"
|
||||
// S-SPARC: "-o" "a.out" "{{.*}}/usr/lib{{/|\\\\}}crt0.o"
|
||||
|
@ -13,12 +13,12 @@
|
||||
// In the presence of -nostdlib, the standard libraries should not be
|
||||
// passed down to link line
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -Wno-liblto -o %t.o 2>&1 \
|
||||
// RUN: -target i686-pc-linux-gnu -nostdlib --rtlib=compiler-rt \
|
||||
// RUN: -target i686-pc-linux-gnu -nostdlib --rtlib=compiler-rt -fuse-ld=ld \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir -lclang_rt.builtins-i686 \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-LINUX-NOSTDLIB %s
|
||||
//
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -Wno-liblto -o %t.o 2>&1 \
|
||||
// RUN: -target i686-pc-linux-gnu --rtlib=compiler-rt -nostdlib \
|
||||
// RUN: -target i686-pc-linux-gnu --rtlib=compiler-rt -nostdlib -fuse-ld=ld \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir -lclang_rt.builtins-i686 \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-LINUX-NOSTDLIB %s
|
||||
//
|
||||
|
@ -1,8 +1,8 @@
|
||||
// RUN: %clang -### -B%S/Inputs/prefixed_tools_tree -o %t.o -no-integrated-as \
|
||||
// RUN: %clang -### -B%S/Inputs/prefixed_tools_tree -o %t.o -no-integrated-as -fuse-ld=ld \
|
||||
// RUN: -target x86_64--linux %s 2>&1 | \
|
||||
// RUN: FileCheck --check-prefix=CHECK-M64 %s
|
||||
|
||||
// RUN: %clang -### -B%S/Inputs/prefixed_tools_tree -o %t.o -no-integrated-as \
|
||||
// RUN: %clang -### -B%S/Inputs/prefixed_tools_tree -o %t.o -no-integrated-as -fuse-ld=ld \
|
||||
// RUN: -m32 -target x86_64--linux %s 2>&1 | \
|
||||
// RUN: FileCheck --check-prefix=CHECK-M32 %s
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Test sanitizers ld flags.
|
||||
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target i386-unknown-linux -fsanitize=address \
|
||||
// RUN: -target i386-unknown-linux -fuse-ld=ld -fsanitize=address \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-ASAN-LINUX %s
|
||||
@ -17,7 +17,7 @@
|
||||
// CHECK-ASAN-LINUX: "-ldl"
|
||||
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target i386-unknown-linux -fsanitize=address -shared-libasan \
|
||||
// RUN: -target i386-unknown-linux -fuse-ld=ld -fsanitize=address -shared-libasan \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-SHARED-ASAN-LINUX %s
|
||||
@ -34,7 +34,7 @@
|
||||
// CHECK-SHARED-ASAN-LINUX-NOT: "--dynamic-list"
|
||||
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.so -shared 2>&1 \
|
||||
// RUN: -target i386-unknown-linux -fsanitize=address -shared-libasan \
|
||||
// RUN: -target i386-unknown-linux -fuse-ld=ld -fsanitize=address -shared-libasan \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-DSO-SHARED-ASAN-LINUX %s
|
||||
@ -51,7 +51,7 @@
|
||||
// CHECK-DSO-SHARED-ASAN-LINUX-NOT: "--dynamic-list"
|
||||
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target i386-unknown-freebsd -fsanitize=address \
|
||||
// RUN: -target i386-unknown-freebsd -fuse-ld=ld -fsanitize=address \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir \
|
||||
// RUN: --sysroot=%S/Inputs/basic_freebsd_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-ASAN-FREEBSD %s
|
||||
@ -67,7 +67,7 @@
|
||||
// CHECK-ASAN-FREEBSD: "-lrt"
|
||||
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target i386-unknown-freebsd -fsanitize=address \
|
||||
// RUN: -target i386-unknown-freebsd -fuse-ld=ld -fsanitize=address \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir \
|
||||
// RUN: --sysroot=%S/Inputs/basic_freebsd_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-ASAN-FREEBSD-LDL %s
|
||||
@ -76,7 +76,7 @@
|
||||
// CHECK-ASAN-FREEBSD-LDL-NOT: "-ldl"
|
||||
|
||||
// RUN: %clangxx -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target i386-unknown-linux -stdlib=platform -fsanitize=address \
|
||||
// RUN: -target i386-unknown-linux -fuse-ld=ld -stdlib=platform -fsanitize=address \
|
||||
// RUN: -resource-dir=%S/Inputs/empty_resource_dir \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-ASAN-LINUX-CXX %s
|
||||
@ -93,7 +93,7 @@
|
||||
// CHECK-ASAN-LINUX-CXX: "-ldl"
|
||||
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o /dev/null -fsanitize=address \
|
||||
// RUN: -target i386-unknown-linux -stdlib=platform \
|
||||
// RUN: -target i386-unknown-linux -fuse-ld=ld -stdlib=platform \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree -lstdc++ -static 2>&1 \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-ASAN-LINUX-CXX-STATIC %s
|
||||
//
|
||||
@ -103,7 +103,7 @@
|
||||
// CHECK-ASAN-LINUX-CXX-STATIC: stdc++
|
||||
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target arm-linux-gnueabi -fsanitize=address \
|
||||
// RUN: -target arm-linux-gnueabi -fuse-ld=ld -fsanitize=address \
|
||||
// RUN: --sysroot=%S/Inputs/basic_android_tree/sysroot \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-ASAN-ARM %s
|
||||
//
|
||||
@ -112,7 +112,7 @@
|
||||
// CHECK-ASAN-ARM: libclang_rt.asan-arm.a"
|
||||
//
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target armv7l-linux-gnueabi -fsanitize=address \
|
||||
// RUN: -target armv7l-linux-gnueabi -fuse-ld=ld -fsanitize=address \
|
||||
// RUN: --sysroot=%S/Inputs/basic_android_tree/sysroot \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-ASAN-ARMv7 %s
|
||||
//
|
||||
@ -121,7 +121,7 @@
|
||||
// CHECK-ASAN-ARMv7: libclang_rt.asan-arm.a"
|
||||
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target arm-linux-androideabi -fsanitize=address \
|
||||
// RUN: -target arm-linux-androideabi -fuse-ld=ld -fsanitize=address \
|
||||
// RUN: --sysroot=%S/Inputs/basic_android_tree/sysroot \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-ASAN-ANDROID %s
|
||||
//
|
||||
@ -141,7 +141,7 @@
|
||||
// CHECK-ASAN-ANDROID-SHARED-LIBASAN-NOT: argument unused during compilation: '-shared-libasan'
|
||||
//
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target arm-linux-androideabi -fsanitize=address \
|
||||
// RUN: -target arm-linux-androideabi -fuse-ld=ld -fsanitize=address \
|
||||
// RUN: --sysroot=%S/Inputs/basic_android_tree/sysroot \
|
||||
// RUN: -shared \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-ASAN-ANDROID-SHARED %s
|
||||
@ -152,7 +152,7 @@
|
||||
// CHECK-ASAN-ANDROID-SHARED-NOT: "-lpthread"
|
||||
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target sparcel-myriad-rtems-elf -fsanitize=address \
|
||||
// RUN: -target sparcel-myriad-rtems-elf -fuse-ld=ld -fsanitize=address \
|
||||
// RUN: --sysroot=%S/Inputs/basic_myriad_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-ASAN-MYRIAD %s
|
||||
//
|
||||
@ -161,7 +161,7 @@
|
||||
// CHECK-ASAN-MYRIAD: libclang_rt.asan-sparcel.a"
|
||||
|
||||
// RUN: %clangxx -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-unknown-linux -stdlib=platform -lstdc++ \
|
||||
// RUN: -target x86_64-unknown-linux -fuse-ld=ld -stdlib=platform -lstdc++ \
|
||||
// RUN: -fsanitize=thread \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
@ -180,7 +180,7 @@
|
||||
// CHECK-TSAN-LINUX-CXX: "-ldl"
|
||||
|
||||
// RUN: %clangxx -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-unknown-linux -stdlib=platform -lstdc++ \
|
||||
// RUN: -target x86_64-unknown-linux -fuse-ld=ld -stdlib=platform -lstdc++ \
|
||||
// RUN: -fsanitize=memory \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
@ -199,7 +199,7 @@
|
||||
// CHECK-MSAN-LINUX-CXX: "-ldl"
|
||||
|
||||
// RUN: %clang -fsanitize=undefined %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target i386-unknown-linux \
|
||||
// RUN: -target i386-unknown-linux -fuse-ld=ld \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-UBSAN-LINUX %s
|
||||
// CHECK-UBSAN-LINUX: "{{.*}}ld{{(.exe)?}}"
|
||||
@ -220,7 +220,7 @@
|
||||
// CHECK-UBSAN-LINUX-LINK-CXX-NOT: "-lstdc++"
|
||||
|
||||
// RUN: %clangxx -fsanitize=undefined %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target i386-unknown-linux -stdlib=platform \
|
||||
// RUN: -target i386-unknown-linux -fuse-ld=ld -stdlib=platform \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-UBSAN-LINUX-CXX %s
|
||||
@ -235,7 +235,7 @@
|
||||
// CHECK-UBSAN-LINUX-CXX: "-lpthread"
|
||||
|
||||
// RUN: %clang -fsanitize=address,undefined %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target i386-unknown-linux \
|
||||
// RUN: -target i386-unknown-linux -fuse-ld=ld \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-ASAN-UBSAN-LINUX %s
|
||||
// CHECK-ASAN-UBSAN-LINUX: "{{.*}}ld{{(.exe)?}}"
|
||||
@ -245,7 +245,7 @@
|
||||
// CHECK-ASAN-UBSAN-LINUX: "-lpthread"
|
||||
|
||||
// RUN: %clangxx -fsanitize=address,undefined %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target i386-unknown-linux -stdlib=platform \
|
||||
// RUN: -target i386-unknown-linux -fuse-ld=ld -stdlib=platform \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-ASAN-UBSAN-LINUX-CXX %s
|
||||
// CHECK-ASAN-UBSAN-LINUX-CXX: "{{.*}}ld{{(.exe)?}}"
|
||||
@ -256,7 +256,7 @@
|
||||
// CHECK-ASAN-UBSAN-LINUX-CXX: "-lpthread"
|
||||
|
||||
// RUN: %clangxx -fsanitize=memory,undefined %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-unknown-linux \
|
||||
// RUN: -target x86_64-unknown-linux -fuse-ld=ld \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-MSAN-UBSAN-LINUX-CXX %s
|
||||
// CHECK-MSAN-UBSAN-LINUX-CXX: "{{.*}}ld{{(.exe)?}}"
|
||||
@ -264,7 +264,7 @@
|
||||
// CHECK-MSAN-UBSAN-LINUX-CXX-NOT: libclang_rt.ubsan
|
||||
|
||||
// RUN: %clangxx -fsanitize=thread,undefined %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-unknown-linux \
|
||||
// RUN: -target x86_64-unknown-linux -fuse-ld=ld \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-TSAN-UBSAN-LINUX-CXX %s
|
||||
// CHECK-TSAN-UBSAN-LINUX-CXX: "{{.*}}ld{{(.exe)?}}"
|
||||
@ -272,7 +272,7 @@
|
||||
// CHECK-TSAN-UBSAN-LINUX-CXX-NOT: libclang_rt.ubsan
|
||||
|
||||
// RUN: %clang -fsanitize=undefined %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target i386-unknown-linux \
|
||||
// RUN: -target i386-unknown-linux -fuse-ld=ld \
|
||||
// RUN: -resource-dir=%S/Inputs/resource_dir \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: -shared \
|
||||
@ -283,7 +283,7 @@
|
||||
// CHECK-UBSAN-LINUX-SHARED-NOT: libclang_rt.ubsan
|
||||
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-unknown-linux -fsanitize=leak \
|
||||
// RUN: -target x86_64-unknown-linux -fuse-ld=ld -fsanitize=leak \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-LSAN-LINUX %s
|
||||
//
|
||||
@ -295,7 +295,7 @@
|
||||
// CHECK-LSAN-LINUX: "-ldl"
|
||||
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-unknown-linux -fsanitize=leak -fsanitize-coverage=func \
|
||||
// RUN: -target x86_64-unknown-linux -fuse-ld=ld -fsanitize=leak -fsanitize-coverage=func \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-LSAN-COV-LINUX %s
|
||||
//
|
||||
@ -308,7 +308,7 @@
|
||||
// CHECK-LSAN-COV-LINUX: "-ldl"
|
||||
|
||||
// RUN: %clang -fsanitize=leak,address %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-unknown-linux \
|
||||
// RUN: -target x86_64-unknown-linux -fuse-ld=ld \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-LSAN-ASAN-LINUX %s
|
||||
// CHECK-LSAN-ASAN-LINUX: "{{.*}}ld{{(.exe)?}}"
|
||||
@ -317,7 +317,7 @@
|
||||
// CHECK-LSAN-ASAN-LINUX-NOT: libclang_rt.lsan
|
||||
|
||||
// RUN: %clang -fsanitize=address -fsanitize-coverage=func %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-unknown-linux \
|
||||
// RUN: -target x86_64-unknown-linux -fuse-ld=ld \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-ASAN-COV-LINUX %s
|
||||
// CHECK-ASAN-COV-LINUX: "{{.*}}ld{{(.exe)?}}"
|
||||
@ -327,7 +327,7 @@
|
||||
// CHECK-ASAN-COV-LINUX: "-lpthread"
|
||||
|
||||
// RUN: %clang -fsanitize=memory -fsanitize-coverage=func %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-unknown-linux \
|
||||
// RUN: -target x86_64-unknown-linux -fuse-ld=ld \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-MSAN-COV-LINUX %s
|
||||
// CHECK-MSAN-COV-LINUX: "{{.*}}ld{{(.exe)?}}"
|
||||
@ -337,7 +337,7 @@
|
||||
// CHECK-MSAN-COV-LINUX: "-lpthread"
|
||||
|
||||
// RUN: %clang -fsanitize=dataflow -fsanitize-coverage=func %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-unknown-linux \
|
||||
// RUN: -target x86_64-unknown-linux -fuse-ld=ld \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-DFSAN-COV-LINUX %s
|
||||
// CHECK-DFSAN-COV-LINUX: "{{.*}}ld{{(.exe)?}}"
|
||||
@ -347,7 +347,7 @@
|
||||
// CHECK-DFSAN-COV-LINUX: "-lpthread"
|
||||
|
||||
// RUN: %clang -fsanitize=undefined -fsanitize-coverage=func %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-unknown-linux \
|
||||
// RUN: -target x86_64-unknown-linux -fuse-ld=ld \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-UBSAN-COV-LINUX %s
|
||||
// CHECK-UBSAN-COV-LINUX: "{{.*}}ld{{(.exe)?}}"
|
||||
@ -356,7 +356,7 @@
|
||||
// CHECK-UBSAN-COV-LINUX: "-lpthread"
|
||||
|
||||
// RUN: %clang -fsanitize-coverage=func %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-unknown-linux \
|
||||
// RUN: -target x86_64-unknown-linux -fuse-ld=ld \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-COV-LINUX %s
|
||||
// CHECK-COV-LINUX: "{{.*}}ld{{(.exe)?}}"
|
||||
@ -366,7 +366,7 @@
|
||||
|
||||
// CFI by itself does not link runtime libraries.
|
||||
// RUN: %clang -fsanitize=cfi %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-unknown-linux -rtlib=platform \
|
||||
// RUN: -target x86_64-unknown-linux -fuse-ld=ld -rtlib=platform \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-CFI-LINUX %s
|
||||
// CHECK-CFI-LINUX: "{{.*}}ld{{(.exe)?}}"
|
||||
@ -375,7 +375,7 @@
|
||||
// CFI with diagnostics links the UBSan runtime.
|
||||
// RUN: %clang -fsanitize=cfi -fno-sanitize-trap=cfi -fsanitize-recover=cfi \
|
||||
// RUN: %s -### -o %t.o 2>&1\
|
||||
// RUN: -target x86_64-unknown-linux \
|
||||
// RUN: -target x86_64-unknown-linux -fuse-ld=ld \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-CFI-DIAG-LINUX %s
|
||||
// CHECK-CFI-DIAG-LINUX: "{{.*}}ld{{(.exe)?}}"
|
||||
@ -383,7 +383,7 @@
|
||||
|
||||
// Cross-DSO CFI links the CFI runtime.
|
||||
// RUN: %clang -fsanitize=cfi -fsanitize-cfi-cross-dso %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-unknown-linux \
|
||||
// RUN: -target x86_64-unknown-linux -fuse-ld=ld \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-CFI-CROSS-DSO-LINUX %s
|
||||
// CHECK-CFI-CROSS-DSO-LINUX: "{{.*}}ld{{(.exe)?}}"
|
||||
@ -393,7 +393,7 @@
|
||||
// Cross-DSO CFI with diagnostics links just the CFI runtime.
|
||||
// RUN: %clang -fsanitize=cfi -fsanitize-cfi-cross-dso %s -### -o %t.o 2>&1 \
|
||||
// RUN: -fno-sanitize-trap=cfi -fsanitize-recover=cfi \
|
||||
// RUN: -target x86_64-unknown-linux \
|
||||
// RUN: -target x86_64-unknown-linux -fuse-ld=ld \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-CFI-CROSS-DSO-DIAG-LINUX %s
|
||||
// CHECK-CFI-CROSS-DSO-DIAG-LINUX: "{{.*}}ld{{(.exe)?}}"
|
||||
@ -402,7 +402,7 @@
|
||||
|
||||
// RUN: %clangxx -fsanitize=address %s -### -o %t.o 2>&1 \
|
||||
// RUN: -mmacosx-version-min=10.6 \
|
||||
// RUN: -target x86_64-apple-darwin13.4.0 -stdlib=platform \
|
||||
// RUN: -target x86_64-apple-darwin13.4.0 -fuse-ld=ld -stdlib=platform \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-ASAN-DARWIN106-CXX %s
|
||||
// CHECK-ASAN-DARWIN106-CXX: "{{.*}}ld{{(.exe)?}}"
|
||||
@ -410,7 +410,7 @@
|
||||
// CHECK-ASAN-DARWIN106-CXX-NOT: -lc++abi
|
||||
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-unknown-linux -fsanitize=safe-stack \
|
||||
// RUN: -target x86_64-unknown-linux -fuse-ld=ld -fsanitize=safe-stack \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-SAFESTACK-LINUX %s
|
||||
//
|
||||
@ -421,7 +421,7 @@
|
||||
// CHECK-SAFESTACK-LINUX: "-ldl"
|
||||
|
||||
// RUN: %clang -fsanitize=cfi -fsanitize-stats %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-unknown-linux \
|
||||
// RUN: -target x86_64-unknown-linux -fuse-ld=ld \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-CFI-STATS-LINUX %s
|
||||
// CHECK-CFI-STATS-LINUX: "{{.*}}ld{{(.exe)?}}"
|
||||
@ -430,7 +430,7 @@
|
||||
// CHECK-CFI-STATS-LINUX: "{{[^"]*}}libclang_rt.stats-x86_64.a"
|
||||
|
||||
// RUN: %clang -fsanitize=cfi -fsanitize-stats %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-apple-darwin \
|
||||
// RUN: -target x86_64-apple-darwin -fuse-ld=ld \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-CFI-STATS-DARWIN %s
|
||||
// CHECK-CFI-STATS-DARWIN: "{{.*}}ld{{(.exe)?}}"
|
||||
@ -454,7 +454,7 @@
|
||||
// CHECK-CFI-STATS-WIN32: "--linker-option=/include:___sanitizer_stats_register"
|
||||
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target arm-linux-androideabi -fsanitize=safe-stack \
|
||||
// RUN: -target arm-linux-androideabi -fuse-ld=ld -fsanitize=safe-stack \
|
||||
// RUN: --sysroot=%S/Inputs/basic_android_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-SAFESTACK-ANDROID-ARM %s
|
||||
//
|
||||
@ -462,7 +462,7 @@
|
||||
// CHECK-SAFESTACK-ANDROID-ARM-NOT: libclang_rt.safestack
|
||||
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o -shared 2>&1 \
|
||||
// RUN: -target arm-linux-androideabi -fsanitize=safe-stack \
|
||||
// RUN: -target arm-linux-androideabi -fuse-ld=ld -fsanitize=safe-stack \
|
||||
// RUN: --sysroot=%S/Inputs/basic_android_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-SAFESTACK-SHARED-ANDROID-ARM %s
|
||||
//
|
||||
@ -470,7 +470,7 @@
|
||||
// CHECK-SAFESTACK-SHARED-ANDROID-ARM-NOT: libclang_rt.safestack
|
||||
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target aarch64-linux-android -fsanitize=safe-stack \
|
||||
// RUN: -target aarch64-linux-android -fuse-ld=ld -fsanitize=safe-stack \
|
||||
// RUN: --sysroot=%S/Inputs/basic_android_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-SAFESTACK-ANDROID-AARCH64 %s
|
||||
//
|
||||
@ -478,7 +478,7 @@
|
||||
// CHECK-SAFESTACK-ANDROID-AARCH64-NOT: libclang_rt.safestack
|
||||
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target arm-linux-androideabi -fsanitize=cfi \
|
||||
// RUN: -target arm-linux-androideabi -fuse-ld=ld -fsanitize=cfi \
|
||||
// RUN: --sysroot=%S/Inputs/basic_android_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-CFI-ANDROID %s
|
||||
//
|
||||
@ -487,7 +487,7 @@
|
||||
// CHECK-CFI-ANDROID-NOT: __cfi_check
|
||||
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target arm-linux-androideabi -fsanitize=cfi \
|
||||
// RUN: -target arm-linux-androideabi -fuse-ld=ld -fsanitize=cfi \
|
||||
// RUN: -fsanitize-cfi-cross-dso \
|
||||
// RUN: --sysroot=%S/Inputs/basic_android_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-CROSSDSO-CFI-ANDROID %s
|
||||
@ -498,31 +498,31 @@
|
||||
// CHECK-CROSSDSO-CFI-ANDROID-NOT: libclang_rt.cfi
|
||||
|
||||
// RUN: %clang -fsanitize=undefined %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-scei-ps4 \
|
||||
// RUN: -target x86_64-scei-ps4 -fuse-ld=ld \
|
||||
// RUN: -shared \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-UBSAN-PS4 %s
|
||||
// CHECK-UBSAN-PS4: "{{.*}}ld{{(.gold)?(.exe)?}}"
|
||||
// CHECK-UBSAN-PS4: -lSceDbgUBSanitizer_stub_weak
|
||||
|
||||
// RUN: %clang -fsanitize=address %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-scei-ps4 \
|
||||
// RUN: -target x86_64-scei-ps4 -fuse-ld=ld \
|
||||
// RUN: -shared \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-ASAN-PS4 %s
|
||||
// CHECK-ASAN-PS4: "{{.*}}ld{{(.gold)?(.exe)?}}"
|
||||
// CHECK-ASAN-PS4: -lSceDbgAddressSanitizer_stub_weak
|
||||
|
||||
// RUN: %clang -fsanitize=address,undefined %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-scei-ps4 \
|
||||
// RUN: -target x86_64-scei-ps4 -fuse-ld=ld \
|
||||
// RUN: -shared \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-AUBSAN-PS4 %s
|
||||
// CHECK-AUBSAN-PS4: "{{.*}}ld{{(.gold)?(.exe)?}}"
|
||||
// CHECK-AUBSAN-PS4: -lSceDbgAddressSanitizer_stub_weak
|
||||
|
||||
// RUN: %clang -fsanitize=efficiency-cache-frag %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-unknown-linux \
|
||||
// RUN: -target x86_64-unknown-linux -fuse-ld=ld \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-ESAN-LINUX %s
|
||||
// RUN: %clang -fsanitize=efficiency-working-set %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target x86_64-unknown-linux \
|
||||
// RUN: -target x86_64-unknown-linux -fuse-ld=ld \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-ESAN-LINUX %s
|
||||
//
|
||||
// CHECK-ESAN-LINUX: "{{(.*[^-.0-9A-Z_a-z])?}}ld{{(.exe)?}}"
|
||||
|
@ -1,34 +1,34 @@
|
||||
// RUN: %clang -### -target armv7-windows-itanium --sysroot %S/Inputs/Windows/ARM/8.1 -B %S/Inputs/Windows/ARM/8.1/usr/bin -stdlib=libstdc++ -rtlib=platform -o /dev/null %s 2>&1 \
|
||||
// RUN: %clang -### -target armv7-windows-itanium --sysroot %S/Inputs/Windows/ARM/8.1 -B %S/Inputs/Windows/ARM/8.1/usr/bin -fuse-ld=ld -stdlib=libstdc++ -rtlib=platform -o /dev/null %s 2>&1 \
|
||||
// RUN: | FileCheck %s --check-prefix CHECK-BASIC
|
||||
|
||||
// CHECK-BASIC: armv7-windows-itanium-ld" "--sysroot={{.*}}/Inputs/Windows/ARM/8.1" "-m" "thumb2pe" "-Bdynamic" "--entry" "mainCRTStartup" "--allow-multiple-definition" "-o" "{{[^"]*}}" "{{.*}}/Inputs/Windows/ARM/8.1/usr/lib/crtbegin.obj" "-L{{.*}}/Inputs/Windows/ARM/8.1/usr/lib" "-L{{.*}}/Inputs/Windows/ARM/8.1/usr/lib/gcc" "{{.*}}.o" "-lmsvcrt" "-lgcc" "--as-needed" "-lgcc_s" "--no-as-needed"
|
||||
|
||||
// RUN: %clang -### -target armv7-windows-itanium --sysroot %s/Inputs/Windows/ARM/8.1 -B %S/Inputs/Windows/ARM/8.1/usr/bin -rtlib=compiler-rt -stdlib=libstdc++ -o /dev/null %s 2>&1 \
|
||||
// RUN: %clang -### -target armv7-windows-itanium --sysroot %s/Inputs/Windows/ARM/8.1 -B %S/Inputs/Windows/ARM/8.1/usr/bin -fuse-ld=ld -rtlib=compiler-rt -stdlib=libstdc++ -o /dev/null %s 2>&1 \
|
||||
// RUN: | FileCheck %s --check-prefix CHECK-RTLIB
|
||||
|
||||
// CHECK-RTLIB: armv7-windows-itanium-ld" "--sysroot={{.*}}/Inputs/Windows/ARM/8.1" "-m" "thumb2pe" "-Bdynamic" "--entry" "mainCRTStartup" "--allow-multiple-definition" "-o" "{{[^"]*}}" "{{.*}}/Inputs/Windows/ARM/8.1/usr/lib/crtbegin.obj" "-L{{.*}}/Inputs/Windows/ARM/8.1/usr/lib" "-L{{.*}}/Inputs/Windows/ARM/8.1/usr/lib/gcc" "{{.*}}.o" "-lmsvcrt" "{{.*[\\/]}}clang_rt.builtins-arm.lib"
|
||||
|
||||
// RUN: %clang -### -target armv7-windows-itanium --sysroot %S/Inputs/Windows/ARM/8.1 -B %S/Inputs/Windows/ARM/8.1/usr/bin -rtlib=compiler-rt -stdlib=libc++ -o /dev/null %s 2>&1 \
|
||||
// RUN: %clang -### -target armv7-windows-itanium --sysroot %S/Inputs/Windows/ARM/8.1 -B %S/Inputs/Windows/ARM/8.1/usr/bin -fuse-ld=ld -rtlib=compiler-rt -stdlib=libc++ -o /dev/null %s 2>&1 \
|
||||
// RUN: | FileCheck %s --check-prefix CHECK-C-LIBCXX
|
||||
|
||||
// CHECK-C-LIBCXX: armv7-windows-itanium-ld" "--sysroot={{.*}}/Inputs/Windows/ARM/8.1" "-m" "thumb2pe" "-Bdynamic" "--entry" "mainCRTStartup" "--allow-multiple-definition" "-o" "{{[^"]*}}" "{{.*}}/Inputs/Windows/ARM/8.1/usr/lib/crtbegin.obj" "{{.*}}.o" "-lmsvcrt" "{{.*[\\/]}}clang_rt.builtins-arm.lib"
|
||||
|
||||
// RUN: %clangxx -### -target armv7-windows-itanium --sysroot %S/Inputs/Windows/ARM/8.1 -B %S/Inputs/Windows/ARM/8.1/usr/bin -rtlib=compiler-rt -stdlib=libc++ -o /dev/null %s 2>&1 \
|
||||
// RUN: %clangxx -### -target armv7-windows-itanium --sysroot %S/Inputs/Windows/ARM/8.1 -B %S/Inputs/Windows/ARM/8.1/usr/bin -fuse-ld=ld -rtlib=compiler-rt -stdlib=libc++ -o /dev/null %s 2>&1 \
|
||||
// RUN: | FileCheck %s --check-prefix CHECK-LIBCXX
|
||||
|
||||
// CHECK-LIBCXX: armv7-windows-itanium-ld" "--sysroot={{.*}}/Inputs/Windows/ARM/8.1" "-m" "thumb2pe" "-Bdynamic" "--entry" "mainCRTStartup" "--allow-multiple-definition" "-o" "{{[^"]*}}" "{{.*}}/Inputs/Windows/ARM/8.1/usr/lib/crtbegin.obj" "{{.*}}.o" "-lc++" "-lmsvcrt" "{{.*[\\/]}}clang_rt.builtins-arm.lib"
|
||||
|
||||
// RUN: %clang -### -target armv7-windows-itanium --sysroot %S/Inputs/Windows/ARM/8.1 -B %S/Inputs/Windows/ARM/8.1/usr/bin -shared -rtlib=compiler-rt -stdlib=libc++ -o shared.dll %s 2>&1 \
|
||||
// RUN: %clang -### -target armv7-windows-itanium --sysroot %S/Inputs/Windows/ARM/8.1 -B %S/Inputs/Windows/ARM/8.1/usr/bin -fuse-ld=ld -shared -rtlib=compiler-rt -stdlib=libc++ -o shared.dll %s 2>&1 \
|
||||
// RUN: | FileCheck %s --check-prefix CHECK-SHARED
|
||||
|
||||
// CHECK-SHARED: armv7-windows-itanium-ld" "--sysroot={{.*}}/Inputs/Windows/ARM/8.1" "-m" "thumb2pe" "-shared" "-Bdynamic" "--enable-auto-image-base" "--entry" "_DllMainCRTStartup" "--allow-multiple-definition" "-o" "shared.dll" "--out-implib" "shared.lib" "{{.*}}/Inputs/Windows/ARM/8.1/usr/lib/crtbeginS.obj" "{{.*}}.o" "-lmsvcrt" "{{.*[\\/]}}clang_rt.builtins-arm.lib"
|
||||
|
||||
// RUN: %clang -### -target armv7-windows-itanium --sysroot %s/Inputs/Windows/ARM/8.1 -B %S/Inputs/Windows/ARM/8.1/usr/bin -shared -rtlib=compiler-rt -stdlib=libc++ -nostartfiles -o shared.dll %s 2>&1 \
|
||||
// RUN: %clang -### -target armv7-windows-itanium --sysroot %s/Inputs/Windows/ARM/8.1 -B %S/Inputs/Windows/ARM/8.1/usr/bin -fuse-ld=ld -shared -rtlib=compiler-rt -stdlib=libc++ -nostartfiles -o shared.dll %s 2>&1 \
|
||||
// RUN: | FileCheck %s --check-prefix CHECK-NOSTARTFILES
|
||||
|
||||
// CHECK-NOSTARTFILES: armv7-windows-itanium-ld" "--sysroot={{.*}}/Inputs/Windows/ARM/8.1" "-m" "thumb2pe" "-shared" "-Bdynamic" "--enable-auto-image-base" "--entry" "_DllMainCRTStartup" "--allow-multiple-definition" "-o" "shared.dll" "--out-implib" "shared.lib" "{{.*}}.o" "-lmsvcrt" "{{.*[\\/]}}clang_rt.builtins-arm.lib"
|
||||
|
||||
// RUN: %clang -### -target armv7-windows-itanium --sysroot %S/Inputs/Windows/ARM/8.1 -B %S/Inputs/Windows/ARM/8.1/usr/bin -shared -rtlib=compiler-rt -stdlib=libc++ -nostartfiles -nodefaultlibs -o shared.dll %s 2>&1 \
|
||||
// RUN: %clang -### -target armv7-windows-itanium --sysroot %S/Inputs/Windows/ARM/8.1 -B %S/Inputs/Windows/ARM/8.1/usr/bin -fuse-ld=ld -shared -rtlib=compiler-rt -stdlib=libc++ -nostartfiles -nodefaultlibs -o shared.dll %s 2>&1 \
|
||||
// RUN: | FileCheck %s --check-prefix CHECK-STANDALONE
|
||||
|
||||
// CHECK-STANDALONE: armv7-windows-itanium-ld" "--sysroot={{.*}}/Inputs/Windows/ARM/8.1" "-m" "thumb2pe" "-shared" "-Bdynamic" "--enable-auto-image-base" "--entry" "_DllMainCRTStartup" "--allow-multiple-definition" "-o" "shared.dll" "--out-implib" "shared.lib" "{{.*}}.o"
|
||||
|
@ -1,5 +1,16 @@
|
||||
// RUN: c-index-test core -print-source-symbols -- %s -std=c++14 -target x86_64-apple-macosx10.7 | FileCheck %s
|
||||
|
||||
// CHECK: [[@LINE+1]]:7 | class/C++ | Cls | c:@S@Cls | <no-cgname> | Def | rel: 0
|
||||
class Cls {
|
||||
// CHECK: [[@LINE+2]]:3 | constructor/C++ | Cls | c:@S@Cls@F@Cls#I# | __ZN3ClsC1Ei | Decl,RelChild | rel: 1
|
||||
// CHECK-NEXT: RelChild | Cls | c:@S@Cls
|
||||
Cls(int x);
|
||||
// CHECK: [[@LINE+1]]:3 | constructor/cxx-copy-ctor/C++ | Cls | c:@S@Cls@F@Cls#&1$@S@Cls# | __ZN3ClsC1ERKS_ | Decl,RelChild | rel: 1
|
||||
Cls(const Cls &);
|
||||
// CHECK: [[@LINE+1]]:3 | constructor/cxx-move-ctor/C++ | Cls | c:@S@Cls@F@Cls#&&$@S@Cls# | __ZN3ClsC1EOS_ | Decl,RelChild | rel: 1
|
||||
Cls(Cls &&);
|
||||
};
|
||||
|
||||
template <typename TemplArg>
|
||||
class TemplCls {
|
||||
// CHECK: [[@LINE-1]]:7 | class(Gen)/C++ | TemplCls | c:@ST>1#T@TemplCls | <no-cgname> | Def | rel: 0
|
||||
|
@ -1265,7 +1265,7 @@ void test() {
|
||||
foo<BoolT<true>>(X);
|
||||
}
|
||||
// CHECK-ELIDE-NOTREE: no matching function for call to 'foo'
|
||||
// CHECK-ELIDE-NOTREE: candidate function [with T = BoolArgumentBitExtended::BoolT<true>] not viable: no known conversion from 'BoolT<false>' to 'BoolT<true>' for 1st argument
|
||||
// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'BoolT<false>' to 'BoolT<true>' for 1st argument
|
||||
}
|
||||
|
||||
namespace DifferentIntegralTypes {
|
||||
@ -1401,7 +1401,7 @@ void run() {
|
||||
f(1, integral_constant<bool, true>{});
|
||||
}
|
||||
// CHECK-ELIDE-NOTREE: error: no matching function for call to 'f'
|
||||
// CHECK-ELIDE-NOTREE: note: candidate function [with T = int] not viable: no known conversion from 'integral_constant<[...], true>' to 'integral_constant<[...], false>' for 2nd argument
|
||||
// CHECK-ELIDE-NOTREE: note: candidate function not viable: no known conversion from 'integral_constant<[...], true>' to 'integral_constant<[...], false>' for 2nd argument
|
||||
}
|
||||
|
||||
namespace ZeroArgs {
|
||||
@ -1454,7 +1454,7 @@ void run() {
|
||||
D<X::X1>(VectorType<X::X2>());
|
||||
}
|
||||
// CHECK-ELIDE-NOTREE: error: no matching function for call to 'D'
|
||||
// CHECK-ELIDE-NOTREE: note: candidate function [with x = TypeAlias::X::X1] not viable: no known conversion from 'VectorType<X::X2>' to 'const VectorType<(TypeAlias::X)0>' for 1st argument
|
||||
// CHECK-ELIDE-NOTREE: note: candidate function not viable: no known conversion from 'VectorType<X::X2>' to 'const VectorType<(TypeAlias::X)0>' for 1st argument
|
||||
}
|
||||
|
||||
namespace TypeAlias2 {
|
||||
|
1
test/Modules/Inputs/pch-with-module-name/A.h
Normal file
1
test/Modules/Inputs/pch-with-module-name/A.h
Normal file
@ -0,0 +1 @@
|
||||
// in pch
|
1
test/Modules/Inputs/pch-with-module-name/C.h
Normal file
1
test/Modules/Inputs/pch-with-module-name/C.h
Normal file
@ -0,0 +1 @@
|
||||
#include "D.h"
|
1
test/Modules/Inputs/pch-with-module-name/C.m
Normal file
1
test/Modules/Inputs/pch-with-module-name/C.m
Normal file
@ -0,0 +1 @@
|
||||
//empty
|
1
test/Modules/Inputs/pch-with-module-name/D.h
Normal file
1
test/Modules/Inputs/pch-with-module-name/D.h
Normal file
@ -0,0 +1 @@
|
||||
//empty
|
@ -0,0 +1,9 @@
|
||||
module CloudKit {
|
||||
header "C.h"
|
||||
export *
|
||||
}
|
||||
|
||||
module Contacts {
|
||||
header "D.h"
|
||||
export *
|
||||
}
|
1
test/Modules/Inputs/pch-with-module-name/test.h
Normal file
1
test/Modules/Inputs/pch-with-module-name/test.h
Normal file
@ -0,0 +1 @@
|
||||
#include "A.h"
|
5
test/Modules/pch-with-module-name.m
Normal file
5
test/Modules/pch-with-module-name.m
Normal file
@ -0,0 +1,5 @@
|
||||
// RUN: rm -rf %t
|
||||
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I %S/Inputs/pch-with-module-name -emit-pch -o %t-A.pch %S/Inputs/pch-with-module-name/test.h -fmodule-name=Contacts -x objective-c-header
|
||||
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I %S/Inputs/pch-with-module-name -include-pch %t-A.pch %s -fsyntax-only -fmodule-name=Contacts -verify
|
||||
// expected-no-diagnostics
|
||||
#include "C.h"
|
@ -1,4 +1,6 @@
|
||||
// RUN: %clang_cc1 -verify -triple x86_64-apple-darwin10 -fopenmp -fexceptions -fcxx-exceptions -x c++ -emit-llvm %s -o - | FileCheck %s
|
||||
// RUN: %clang_cc1 -verify -triple x86_64-apple-darwin10 -fopenmp -fexceptions -fcxx-exceptions -x c++ -emit-llvm -std=c++98 %s -o - | FileCheck %s
|
||||
// RUN: %clang_cc1 -verify -triple x86_64-apple-darwin10 -fopenmp -fexceptions -fcxx-exceptions -x c++ -emit-llvm -std=c++11 %s -o - | FileCheck %s
|
||||
// RUN: %clang_cc1 -verify -triple x86_64-apple-darwin10 -fopenmp -fexceptions -fcxx-exceptions -debug-info-kind=line-tables-only -x c++ -emit-llvm %s -o - | FileCheck %s --check-prefix=TERM_DEBUG
|
||||
// expected-no-diagnostics
|
||||
|
||||
@ -21,14 +23,15 @@ void parallel_atomic_ewc() {
|
||||
// CHECK: [[SCALAR_ADDR:%.+]] = invoke dereferenceable(4) i32* @_ZN2St3getEv(%struct.St* [[TEMP_ST_ADDR]])
|
||||
// CHECK: [[SCALAR_VAL:%.+]] = load atomic i32, i32* [[SCALAR_ADDR]] monotonic
|
||||
// CHECK: store i32 [[SCALAR_VAL]], i32* @b
|
||||
// CHECK: invoke void @_ZN2StD1Ev(%struct.St* [[TEMP_ST_ADDR]])
|
||||
// CHECK98: invoke void @_ZN2StD1Ev(%struct.St* [[TEMP_ST_ADDR]])
|
||||
// CHECK11: call void @_ZN2StD1Ev(%struct.St* [[TEMP_ST_ADDR]])
|
||||
#pragma omp atomic read
|
||||
b = St().get();
|
||||
// CHECK-DAG: invoke void @_ZN2StC1Ev(%struct.St* [[TEMP_ST_ADDR:%.+]])
|
||||
// CHECK-DAG: [[SCALAR_ADDR:%.+]] = invoke dereferenceable(4) i32* @_ZN2St3getEv(%struct.St* [[TEMP_ST_ADDR]])
|
||||
// CHECK-DAG: [[B_VAL:%.+]] = load i32, i32* @b
|
||||
// CHECK: store atomic i32 [[B_VAL]], i32* [[SCALAR_ADDR]] monotonic
|
||||
// CHECK: invoke void @_ZN2StD1Ev(%struct.St* [[TEMP_ST_ADDR]])
|
||||
// CHECK: {{invoke|call}} void @_ZN2StD1Ev(%struct.St* [[TEMP_ST_ADDR]])
|
||||
#pragma omp atomic write
|
||||
St().get() = b;
|
||||
// CHECK: invoke void @_ZN2StC1Ev(%struct.St* [[TEMP_ST_ADDR:%.+]])
|
||||
@ -46,7 +49,7 @@ void parallel_atomic_ewc() {
|
||||
// CHECK: [[COND:%.+]] = extractvalue { i32, i1 } [[RES]], 1
|
||||
// CHECK: br i1 [[COND]], label %[[OMP_DONE:.+]], label %[[OMP_UPDATE]]
|
||||
// CHECK: [[OMP_DONE]]
|
||||
// CHECK: invoke void @_ZN2StD1Ev(%struct.St* [[TEMP_ST_ADDR]])
|
||||
// CHECK: {{invoke|call}} void @_ZN2StD1Ev(%struct.St* [[TEMP_ST_ADDR]])
|
||||
#pragma omp atomic
|
||||
St().get() %= b;
|
||||
#pragma omp atomic
|
||||
@ -67,7 +70,7 @@ void parallel_atomic_ewc() {
|
||||
// CHECK: br i1 [[COND]], label %[[OMP_DONE:.+]], label %[[OMP_UPDATE]]
|
||||
// CHECK: [[OMP_DONE]]
|
||||
// CHECK: store i32 [[NEW_CALC_VAL]], i32* @a,
|
||||
// CHECK: invoke void @_ZN2StD1Ev(%struct.St* [[TEMP_ST_ADDR]])
|
||||
// CHECK: {{invoke|call}} void @_ZN2StD1Ev(%struct.St* [[TEMP_ST_ADDR]])
|
||||
#pragma omp atomic capture
|
||||
a = St().get() %= b;
|
||||
}
|
||||
|
@ -275,7 +275,7 @@ S1 arr_x[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } };
|
||||
// CHECK: {{.*}}[[ARR_LOOP]]{{.*}}
|
||||
// CHECK-NEXT: [[ARR_ELEMENTPAST:%.*]] = phi [[S1]]* [ [[ARR_CUR]], {{.*}} ], [ [[ARR_ELEMENT:%.*]], {{.*}} ]
|
||||
// CHECK-NEXT: [[ARR_ELEMENT:%.*]] = getelementptr inbounds [[S1]], [[S1]]* [[ARR_ELEMENTPAST]], i{{.*}} -1
|
||||
// CHECK-NEXT: invoke {{.*}} [[S1_DTOR]]([[S1]]* [[ARR_ELEMENT]])
|
||||
// CHECK-NEXT: {{call|invoke}} {{.*}} [[S1_DTOR]]([[S1]]* [[ARR_ELEMENT]])
|
||||
// CHECK: [[ARR_DONE:%.*]] = icmp eq [[S1]]* [[ARR_ELEMENT]], [[ARR_BEGIN]]
|
||||
// CHECK-NEXT: br i1 [[ARR_DONE]], label %[[ARR_EXIT:.*]], label %[[ARR_LOOP]]
|
||||
// CHECK: {{.*}}[[ARR_EXIT]]{{.*}}
|
||||
|
152
test/Sema/diagnose_if.c
Normal file
152
test/Sema/diagnose_if.c
Normal file
@ -0,0 +1,152 @@
|
||||
// RUN: %clang_cc1 %s -verify -fno-builtin
|
||||
|
||||
#define _diagnose_if(...) __attribute__((diagnose_if(__VA_ARGS__)))
|
||||
|
||||
void failure() _diagnose_if(); // expected-error{{exactly 3 arguments}}
|
||||
void failure() _diagnose_if(0); // expected-error{{exactly 3 arguments}}
|
||||
void failure() _diagnose_if(0, ""); // expected-error{{exactly 3 arguments}}
|
||||
void failure() _diagnose_if(0, "", "error", 1); // expected-error{{exactly 3 arguments}}
|
||||
void failure() _diagnose_if(0, 0, "error"); // expected-error{{requires a string}}
|
||||
void failure() _diagnose_if(0, "", "invalid"); // expected-error{{invalid diagnostic type for 'diagnose_if'; use "error" or "warning" instead}}
|
||||
void failure() _diagnose_if(0, "", "ERROR"); // expected-error{{invalid diagnostic type}}
|
||||
void failure(int a) _diagnose_if(a, "", ""); // expected-error{{invalid diagnostic type}}
|
||||
void failure() _diagnose_if(a, "", ""); // expected-error{{undeclared identifier 'a'}}
|
||||
|
||||
int globalVar;
|
||||
void never_constant() _diagnose_if(globalVar, "", "error"); // expected-error{{'diagnose_if' attribute expression never produces a constant expression}} expected-note{{subexpression not valid}}
|
||||
void never_constant() _diagnose_if(globalVar, "", "warning"); // expected-error{{'diagnose_if' attribute expression never produces a constant expression}} expected-note{{subexpression not valid}}
|
||||
|
||||
int alwaysok(int q) _diagnose_if(0, "", "error");
|
||||
int neverok(int q) _diagnose_if(1, "oh no", "error"); // expected-note 5{{from 'diagnose_if' attribute on 'neverok'}}
|
||||
int alwayswarn(int q) _diagnose_if(1, "oh no", "warning"); // expected-note 5{{from 'diagnose_if' attribute}}
|
||||
int neverwarn(int q) _diagnose_if(0, "", "warning");
|
||||
|
||||
void runConstant() {
|
||||
int m;
|
||||
alwaysok(0);
|
||||
alwaysok(1);
|
||||
alwaysok(m);
|
||||
|
||||
{
|
||||
int (*pok)(int) = alwaysok;
|
||||
pok = &alwaysok;
|
||||
}
|
||||
|
||||
neverok(0); // expected-error{{oh no}}
|
||||
neverok(1); // expected-error{{oh no}}
|
||||
neverok(m); // expected-error{{oh no}}
|
||||
{
|
||||
int (*pok)(int) = neverok; // expected-error{{oh no}}
|
||||
pok = &neverok; // expected-error{{oh no}}
|
||||
}
|
||||
|
||||
alwayswarn(0); // expected-warning{{oh no}}
|
||||
alwayswarn(1); // expected-warning{{oh no}}
|
||||
alwayswarn(m); // expected-warning{{oh no}}
|
||||
{
|
||||
int (*pok)(int) = alwayswarn; // expected-warning{{oh no}}
|
||||
pok = &alwayswarn; // expected-warning{{oh no}}
|
||||
}
|
||||
|
||||
neverwarn(0);
|
||||
neverwarn(1);
|
||||
neverwarn(m);
|
||||
{
|
||||
int (*pok)(int) = neverwarn;
|
||||
pok = &neverwarn;
|
||||
}
|
||||
}
|
||||
|
||||
int abs(int q) _diagnose_if(q >= 0, "redundant abs call", "error"); //expected-note{{from 'diagnose_if'}}
|
||||
void runVariable() {
|
||||
int m;
|
||||
abs(-1);
|
||||
abs(1); // expected-error{{redundant abs call}}
|
||||
abs(m);
|
||||
|
||||
int (*pabs)(int) = abs;
|
||||
pabs = &abs;
|
||||
}
|
||||
|
||||
#define _overloadable __attribute__((overloadable))
|
||||
|
||||
int ovl1(const char *n) _overloadable _diagnose_if(n, "oh no", "error"); // expected-note{{oh no}}
|
||||
int ovl1(void *m) _overloadable; // expected-note{{candidate function}}
|
||||
|
||||
int ovl2(const char *n) _overloadable _diagnose_if(n, "oh no", "error"); // expected-note{{candidate function}}
|
||||
int ovl2(char *m) _overloadable; // expected-note{{candidate function}}
|
||||
void overloadsYay() {
|
||||
ovl1((void *)0);
|
||||
ovl1(""); // expected-error{{call to unavailable function}}
|
||||
|
||||
ovl2((void *)0); // expected-error{{ambiguous}}
|
||||
}
|
||||
|
||||
void errorWarnDiagnose1() _diagnose_if(1, "oh no", "error") // expected-note{{from 'diagnose_if'}}
|
||||
_diagnose_if(1, "nop", "warning");
|
||||
void errorWarnDiagnose2() _diagnose_if(1, "oh no", "error") // expected-note{{from 'diagnose_if'}}
|
||||
_diagnose_if(1, "nop", "error");
|
||||
void errorWarnDiagnose3() _diagnose_if(1, "nop", "warning")
|
||||
_diagnose_if(1, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
|
||||
|
||||
void errorWarnDiagnoseArg1(int a) _diagnose_if(a == 1, "oh no", "error") // expected-note{{from 'diagnose_if'}}
|
||||
_diagnose_if(a == 1, "nop", "warning");
|
||||
void errorWarnDiagnoseArg2(int a) _diagnose_if(a == 1, "oh no", "error") // expected-note{{from 'diagnose_if'}}
|
||||
_diagnose_if(a == 1, "nop", "error");
|
||||
void errorWarnDiagnoseArg3(int a) _diagnose_if(a == 1, "nop", "warning")
|
||||
_diagnose_if(a == 1, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
|
||||
|
||||
void runErrorWarnDiagnose() {
|
||||
errorWarnDiagnose1(); // expected-error{{oh no}}
|
||||
errorWarnDiagnose2(); // expected-error{{oh no}}
|
||||
errorWarnDiagnose3(); // expected-error{{oh no}}
|
||||
|
||||
errorWarnDiagnoseArg1(1); // expected-error{{oh no}}
|
||||
errorWarnDiagnoseArg2(1); // expected-error{{oh no}}
|
||||
errorWarnDiagnoseArg3(1); // expected-error{{oh no}}
|
||||
}
|
||||
|
||||
void warnWarnDiagnose() _diagnose_if(1, "oh no!", "warning") _diagnose_if(1, "foo", "warning"); // expected-note 2{{from 'diagnose_if'}}
|
||||
void runWarnWarnDiagnose() {
|
||||
warnWarnDiagnose(); // expected-warning{{oh no!}} expected-warning{{foo}}
|
||||
}
|
||||
|
||||
void declsStackErr1(int a) _diagnose_if(a & 1, "decl1", "error"); // expected-note 2{{from 'diagnose_if'}}
|
||||
void declsStackErr1(int a) _diagnose_if(a & 2, "decl2", "error"); // expected-note{{from 'diagnose_if'}}
|
||||
void declsStackErr2();
|
||||
void declsStackErr2() _diagnose_if(1, "complaint", "error"); // expected-note{{from 'diagnose_if'}}
|
||||
void declsStackErr3() _diagnose_if(1, "complaint", "error"); // expected-note{{from 'diagnose_if'}}
|
||||
void declsStackErr3();
|
||||
void runDeclsStackErr() {
|
||||
declsStackErr1(0);
|
||||
declsStackErr1(1); // expected-error{{decl1}}
|
||||
declsStackErr1(2); // expected-error{{decl2}}
|
||||
declsStackErr1(3); // expected-error{{decl1}}
|
||||
declsStackErr2(); // expected-error{{complaint}}
|
||||
declsStackErr3(); // expected-error{{complaint}}
|
||||
}
|
||||
|
||||
void declsStackWarn1(int a) _diagnose_if(a & 1, "decl1", "warning"); // expected-note 2{{from 'diagnose_if'}}
|
||||
void declsStackWarn1(int a) _diagnose_if(a & 2, "decl2", "warning"); // expected-note 2{{from 'diagnose_if'}}
|
||||
void declsStackWarn2();
|
||||
void declsStackWarn2() _diagnose_if(1, "complaint", "warning"); // expected-note{{from 'diagnose_if'}}
|
||||
void declsStackWarn3() _diagnose_if(1, "complaint", "warning"); // expected-note{{from 'diagnose_if'}}
|
||||
void declsStackWarn3();
|
||||
void runDeclsStackWarn() {
|
||||
declsStackWarn1(0);
|
||||
declsStackWarn1(1); // expected-warning{{decl1}}
|
||||
declsStackWarn1(2); // expected-warning{{decl2}}
|
||||
declsStackWarn1(3); // expected-warning{{decl1}} expected-warning{{decl2}}
|
||||
declsStackWarn2(); // expected-warning{{complaint}}
|
||||
declsStackWarn3(); // expected-warning{{complaint}}
|
||||
}
|
||||
|
||||
void noMsg(int n) _diagnose_if(n, "", "warning"); // expected-note{{from 'diagnose_if'}}
|
||||
void runNoMsg() {
|
||||
noMsg(1); // expected-warning{{<no message provided>}}
|
||||
}
|
||||
|
||||
void alwaysWarnWithArg(int a) _diagnose_if(1 || a, "alwaysWarn", "warning"); // expected-note{{from 'diagnose_if'}}
|
||||
void runAlwaysWarnWithArg(int a) {
|
||||
alwaysWarnWithArg(a); // expected-warning{{alwaysWarn}}
|
||||
}
|
@ -24,6 +24,13 @@ void f() {
|
||||
(void)class_ref<int, int&, U<2>::a>(); // expected-note {{here}}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void not_instantiated() {
|
||||
// These cases (arguably) do not require instantiation of U<i>::a.
|
||||
(void)alias_ref<int, int&, U<3>::a>();
|
||||
(void)func_ref<int, int&, U<4>::a>();
|
||||
(void)class_ref<int, int&, U<5>::a>();
|
||||
};
|
||||
|
||||
template<int N>
|
||||
void fi() {
|
||||
@ -33,7 +40,7 @@ void fi() {
|
||||
};
|
||||
|
||||
int main() {
|
||||
f<int>(); // NOTE: Non-dependent name uses are type-checked at template definition time.
|
||||
f<int>(); // expected-note 3{{here}}
|
||||
fi<10>(); // expected-note 3{{here}}
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ void CheckMachineMode() {
|
||||
|
||||
// Check attributes on function parameters.
|
||||
template <class T1, class T2>
|
||||
void CheckParameters(T1 __attribute__((mode(SI))) paramSI, // expected-note2{{ignored: substitution failure}}
|
||||
void CheckParameters(T1 __attribute__((mode(SI))) paramSI, // expected-note{{ignored: substitution failure}} expected-note-re{{not viable: no known conversion from '{{.*}}' (vector of 4 '{{.*}}' values) to 'EnumType' for 2nd argument}}
|
||||
T1 __attribute__((mode(V4DI))) paramV4DI, // expected-warning{{deprecated}}
|
||||
T2 __attribute__((mode(SF))) paramSF,
|
||||
T2 __attribute__((mode(V4DF))) paramV4DF) { // expected-warning{{deprecated}}
|
||||
|
@ -244,11 +244,11 @@ namespace PR15291 {
|
||||
template <typename T>
|
||||
void qux(T) {}
|
||||
|
||||
// expected-note@+5 {{candidate function [with T = void (*)(int) __attribute__((noreturn))] not viable: no overload of 'baz' matching 'void (*)(int) __attribute__((noreturn))' for 1st argument}}
|
||||
// expected-note@+4 {{candidate function [with T = void (*)(int) __attribute__((noreturn))] not viable: no overload of 'qux' matching 'void (*)(int) __attribute__((noreturn))' for 1st argument}}
|
||||
// expected-note@+3 {{candidate function [with T = void (*)(int) __attribute__((noreturn))] not viable: no overload of 'bar' matching 'void (*)(int) __attribute__((noreturn))' for 1st argument}}
|
||||
// expected-note@+2 {{candidate function [with T = void (*)(int)] not viable: no overload of 'bar' matching 'void (*)(int)' for 1st argument}}
|
||||
// expected-note@+1 {{candidate function [with T = void (int)] not viable: no overload of 'bar' matching 'void (*)(int)' for 1st argument}}
|
||||
// expected-note@+5 {{candidate function not viable: no overload of 'baz' matching 'void (*)(int) __attribute__((noreturn))' for 1st argument}}
|
||||
// expected-note@+4 {{candidate function not viable: no overload of 'qux' matching 'void (*)(int) __attribute__((noreturn))' for 1st argument}}
|
||||
// expected-note@+3 {{candidate function not viable: no overload of 'bar' matching 'void (*)(int) __attribute__((noreturn))' for 1st argument}}
|
||||
// expected-note@+2 {{candidate function not viable: no overload of 'bar' matching 'void (*)(int)' for 1st argument}}
|
||||
// expected-note@+1 {{candidate function not viable: no overload of 'bar' matching 'void (*)(int)' for 1st argument}}
|
||||
template <typename T> void accept_T(T) {}
|
||||
|
||||
// expected-note@+1 {{candidate function not viable: no overload of 'bar' matching 'void (*)(int)' for 1st argument}}
|
||||
|
@ -1902,9 +1902,9 @@ namespace ZeroSizeTypes {
|
||||
namespace BadDefaultInit {
|
||||
template<int N> struct X { static const int n = N; };
|
||||
|
||||
struct A { // expected-error {{default member initializer for 'k' needed within definition of enclosing class}}
|
||||
struct A {
|
||||
int k = // expected-note {{default member initializer declared here}}
|
||||
X<A().k>::n; // expected-error {{not a constant expression}} expected-note {{implicit default constructor for 'BadDefaultInit::A' first required here}}
|
||||
X<A().k>::n; // expected-error {{default member initializer for 'k' needed within definition of enclosing class}}
|
||||
};
|
||||
|
||||
// FIXME: The "constexpr constructor must initialize all members" diagnostic
|
||||
|
@ -59,4 +59,118 @@ void f(char c) { //expected-note{{declared here}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_conversion_function_for_non_capturing_lambdas {
|
||||
|
||||
namespace ns1 {
|
||||
auto L = [](int i) { return i; };
|
||||
constexpr int (*fpi)(int) = L;
|
||||
static_assert(fpi(3) == 3);
|
||||
auto GL = [](auto a) { return a; };
|
||||
|
||||
constexpr char (*fp2)(char) = GL;
|
||||
constexpr double (*fp3)(double) = GL;
|
||||
constexpr const char* (*fp4)(const char*) = GL;
|
||||
static_assert(fp2('3') == '3');
|
||||
static_assert(fp3(3.14) == 3.14);
|
||||
constexpr const char *Str = "abc";
|
||||
static_assert(fp4(Str) == Str);
|
||||
|
||||
auto NCL = [](int i) { static int j; return j; }; //expected-note{{declared here}}
|
||||
constexpr int (*fp5)(int) = NCL;
|
||||
constexpr int I = //expected-error{{must be initialized by a constant expression}}
|
||||
fp5(5); //expected-note{{non-constexpr function}}
|
||||
|
||||
namespace test_dont_always_instantiate_constexpr_templates {
|
||||
|
||||
auto explicit_return_type = [](auto x) -> int { return x.get(); };
|
||||
decltype(explicit_return_type(0)) c; // OK
|
||||
|
||||
auto deduced_return_type = [](auto x) { return x.get(); }; //expected-error{{not a structure or union}}
|
||||
decltype(deduced_return_type(0)) d; //expected-note{{requested here}}
|
||||
|
||||
|
||||
|
||||
} // end ns test_dont_always_instantiate_constexpr_templates
|
||||
} // end ns1
|
||||
|
||||
} // end ns test_conversion_function_for_non_capturing_lambdas
|
||||
|
||||
namespace test_lambda_is_cce {
|
||||
namespace ns1_simple_lambda {
|
||||
|
||||
namespace ns0 {
|
||||
constexpr int I = [](auto a) { return a; }(10);
|
||||
|
||||
static_assert(I == 10);
|
||||
static_assert(10 == [](auto a) { return a; }(10));
|
||||
static_assert(3.14 == [](auto a) { return a; }(3.14));
|
||||
|
||||
} //end ns0
|
||||
|
||||
namespace ns1 {
|
||||
constexpr auto f(int i) {
|
||||
double d = 3.14;
|
||||
auto L = [=](auto a) {
|
||||
int Isz = sizeof(i);
|
||||
return sizeof(i) + sizeof(a) + sizeof(d);
|
||||
};
|
||||
int I = L("abc") + L(nullptr);
|
||||
return L;
|
||||
}
|
||||
constexpr auto L = f(3);
|
||||
constexpr auto M = L("abc") + L(nullptr);
|
||||
|
||||
static_assert(M == sizeof(int) * 2 + sizeof(double) * 2 + sizeof(nullptr) + sizeof(const char*));
|
||||
|
||||
} // end ns1
|
||||
|
||||
namespace ns2 {
|
||||
constexpr auto f(int i) {
|
||||
auto L = [](auto a) { return a + a; };
|
||||
return L;
|
||||
}
|
||||
constexpr auto L = f(3);
|
||||
constexpr int I = L(6);
|
||||
static_assert(I == 12);
|
||||
} // end ns2
|
||||
|
||||
namespace contained_lambdas_call_operator_is_not_constexpr {
|
||||
constexpr auto f(int i) {
|
||||
double d = 3.14;
|
||||
auto L = [=](auto a) { //expected-note{{declared here}}
|
||||
int Isz = sizeof(i);
|
||||
asm("hello");
|
||||
return sizeof(i) + sizeof(a) + sizeof(d);
|
||||
};
|
||||
return L;
|
||||
}
|
||||
|
||||
constexpr auto L = f(3);
|
||||
|
||||
constexpr auto M = // expected-error{{must be initialized by}}
|
||||
L("abc"); //expected-note{{non-constexpr function}}
|
||||
|
||||
} // end ns contained_lambdas_call_operator_is_not_constexpr
|
||||
|
||||
|
||||
|
||||
} // end ns1_simple_lambda
|
||||
|
||||
namespace ns1_unimplemented {
|
||||
namespace ns1_captures {
|
||||
constexpr auto f(int i) {
|
||||
double d = 3.14;
|
||||
auto L = [=](auto a) { //expected-note{{coming soon}}
|
||||
int Isz = i + d;
|
||||
return sizeof(i) + sizeof(a) + sizeof(d);
|
||||
};
|
||||
return L;
|
||||
}
|
||||
constexpr auto M = f(3); //expected-error{{constant expression}} expected-note{{in call to}}
|
||||
} // end ns1_captures
|
||||
} // end ns1_unimplemented
|
||||
|
||||
} // end ns test_lambda_is_cce
|
||||
|
||||
#endif // ndef CPP14_AND_EARLIER
|
||||
|
460
test/SemaCXX/diagnose_if.cpp
Normal file
460
test/SemaCXX/diagnose_if.cpp
Normal file
@ -0,0 +1,460 @@
|
||||
// RUN: %clang_cc1 %s -verify -fno-builtin -std=c++14
|
||||
|
||||
#define _diagnose_if(...) __attribute__((diagnose_if(__VA_ARGS__)))
|
||||
|
||||
namespace type_dependent {
|
||||
template <typename T>
|
||||
void neverok() _diagnose_if(!T(), "oh no", "error") {} // expected-note 4{{from 'diagnose_if'}}
|
||||
|
||||
template <typename T>
|
||||
void alwaysok() _diagnose_if(T(), "oh no", "error") {}
|
||||
|
||||
template <typename T>
|
||||
void alwayswarn() _diagnose_if(!T(), "oh no", "warning") {} // expected-note 4{{from 'diagnose_if'}}
|
||||
|
||||
template <typename T>
|
||||
void neverwarn() _diagnose_if(T(), "oh no", "warning") {}
|
||||
|
||||
void runAll() {
|
||||
alwaysok<int>();
|
||||
alwaysok<int>();
|
||||
|
||||
{
|
||||
void (*pok)() = alwaysok<int>;
|
||||
pok = &alwaysok<int>;
|
||||
}
|
||||
|
||||
neverok<int>(); // expected-error{{oh no}}
|
||||
neverok<short>(); // expected-error{{oh no}}
|
||||
|
||||
{
|
||||
void (*pok)() = neverok<int>; // expected-error{{oh no}}
|
||||
}
|
||||
{
|
||||
void (*pok)();
|
||||
pok = &neverok<int>; // expected-error{{oh no}}
|
||||
}
|
||||
|
||||
alwayswarn<int>(); // expected-warning{{oh no}}
|
||||
alwayswarn<short>(); // expected-warning{{oh no}}
|
||||
{
|
||||
void (*pok)() = alwayswarn<int>; // expected-warning{{oh no}}
|
||||
pok = &alwayswarn<int>; // expected-warning{{oh no}}
|
||||
}
|
||||
|
||||
neverwarn<int>();
|
||||
neverwarn<short>();
|
||||
{
|
||||
void (*pok)() = neverwarn<int>;
|
||||
pok = &neverwarn<int>;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void errorIf(T a) _diagnose_if(T() != a, "oh no", "error") {} // expected-note {{candidate disabled: oh no}}
|
||||
|
||||
template <typename T>
|
||||
void warnIf(T a) _diagnose_if(T() != a, "oh no", "warning") {} // expected-note {{from 'diagnose_if'}}
|
||||
|
||||
void runIf() {
|
||||
errorIf(0);
|
||||
errorIf(1); // expected-error{{call to unavailable function}}
|
||||
|
||||
warnIf(0);
|
||||
warnIf(1); // expected-warning{{oh no}}
|
||||
}
|
||||
}
|
||||
|
||||
namespace value_dependent {
|
||||
template <int N>
|
||||
void neverok() _diagnose_if(N == 0 || N != 0, "oh no", "error") {} // expected-note 4{{from 'diagnose_if'}}
|
||||
|
||||
template <int N>
|
||||
void alwaysok() _diagnose_if(N == 0 && N != 0, "oh no", "error") {}
|
||||
|
||||
template <int N>
|
||||
void alwayswarn() _diagnose_if(N == 0 || N != 0, "oh no", "warning") {} // expected-note 4{{from 'diagnose_if'}}
|
||||
|
||||
template <int N>
|
||||
void neverwarn() _diagnose_if(N == 0 && N != 0, "oh no", "warning") {}
|
||||
|
||||
void runAll() {
|
||||
alwaysok<0>();
|
||||
alwaysok<1>();
|
||||
|
||||
{
|
||||
void (*pok)() = alwaysok<0>;
|
||||
pok = &alwaysok<0>;
|
||||
}
|
||||
|
||||
neverok<0>(); // expected-error{{oh no}}
|
||||
neverok<1>(); // expected-error{{oh no}}
|
||||
|
||||
{
|
||||
void (*pok)() = neverok<0>; // expected-error{{oh no}}
|
||||
}
|
||||
{
|
||||
void (*pok)();
|
||||
pok = &neverok<0>; // expected-error{{oh no}}
|
||||
}
|
||||
|
||||
alwayswarn<0>(); // expected-warning{{oh no}}
|
||||
alwayswarn<1>(); // expected-warning{{oh no}}
|
||||
{
|
||||
void (*pok)() = alwayswarn<0>; // expected-warning{{oh no}}
|
||||
pok = &alwayswarn<0>; // expected-warning{{oh no}}
|
||||
}
|
||||
|
||||
neverwarn<0>();
|
||||
neverwarn<1>();
|
||||
{
|
||||
void (*pok)() = neverwarn<0>;
|
||||
pok = &neverwarn<0>;
|
||||
}
|
||||
}
|
||||
|
||||
template <int N>
|
||||
void errorIf(int a) _diagnose_if(N != a, "oh no", "error") {} // expected-note {{candidate disabled: oh no}}
|
||||
|
||||
template <int N>
|
||||
void warnIf(int a) _diagnose_if(N != a, "oh no", "warning") {} // expected-note {{from 'diagnose_if'}}
|
||||
|
||||
void runIf() {
|
||||
errorIf<0>(0);
|
||||
errorIf<0>(1); // expected-error{{call to unavailable function}}
|
||||
|
||||
warnIf<0>(0);
|
||||
warnIf<0>(1); // expected-warning{{oh no}}
|
||||
}
|
||||
}
|
||||
|
||||
namespace no_overload_interaction {
|
||||
void foo(int) _diagnose_if(1, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
|
||||
void foo(short);
|
||||
|
||||
void bar(int);
|
||||
void bar(short) _diagnose_if(1, "oh no", "error");
|
||||
|
||||
void fooArg(int a) _diagnose_if(a, "oh no", "error"); // expected-note{{candidate disabled: oh no}}
|
||||
void fooArg(short); // expected-note{{candidate function}}
|
||||
|
||||
void barArg(int);
|
||||
void barArg(short a) _diagnose_if(a, "oh no", "error");
|
||||
|
||||
void runAll() {
|
||||
foo(1); // expected-error{{oh no}}
|
||||
bar(1);
|
||||
|
||||
fooArg(1); // expected-error{{call to unavailable function}}
|
||||
barArg(1);
|
||||
|
||||
auto p = foo; // expected-error{{incompatible initializer of type '<overloaded function type>'}}
|
||||
}
|
||||
}
|
||||
|
||||
namespace with_default_args {
|
||||
void foo(int a = 0) _diagnose_if(a, "oh no", "warning"); // expected-note 1{{from 'diagnose_if'}}
|
||||
void bar(int a = 1) _diagnose_if(a, "oh no", "warning"); // expected-note 2{{from 'diagnose_if'}}
|
||||
|
||||
void runAll() {
|
||||
foo();
|
||||
foo(0);
|
||||
foo(1); // expected-warning{{oh no}}
|
||||
|
||||
bar(); // expected-warning{{oh no}}
|
||||
bar(0);
|
||||
bar(1); // expected-warning{{oh no}}
|
||||
}
|
||||
}
|
||||
|
||||
namespace naked_mem_expr {
|
||||
struct Foo {
|
||||
void foo(int a) _diagnose_if(a, "should warn", "warning"); // expected-note{{from 'diagnose_if'}}
|
||||
void bar(int a) _diagnose_if(a, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
|
||||
};
|
||||
|
||||
void runFoo() {
|
||||
Foo().foo(0);
|
||||
Foo().foo(1); // expected-warning{{should warn}}
|
||||
|
||||
Foo().bar(0);
|
||||
Foo().bar(1); // expected-error{{oh no}}
|
||||
}
|
||||
}
|
||||
|
||||
namespace class_template {
|
||||
template <typename T>
|
||||
struct Errors {
|
||||
void foo(int i) _diagnose_if(i, "bad i", "error"); // expected-note{{from 'diagnose_if'}}
|
||||
void bar(int i) _diagnose_if(i != T(), "bad i", "error"); // expected-note{{from 'diagnose_if'}}
|
||||
|
||||
void fooOvl(int i) _diagnose_if(i, "int bad i", "error"); // expected-note 2{{int bad i}}
|
||||
void fooOvl(short i) _diagnose_if(i, "short bad i", "error"); // expected-note 2{{short bad i}}
|
||||
|
||||
void barOvl(int i) _diagnose_if(i != T(), "int bad i", "error"); // expected-note 2{{int bad i}}
|
||||
void barOvl(short i) _diagnose_if(i != T(), "short bad i", "error"); // expected-note 2{{short bad i}}
|
||||
};
|
||||
|
||||
void runErrors() {
|
||||
Errors<int>().foo(0);
|
||||
Errors<int>().foo(1); // expected-error{{bad i}}
|
||||
|
||||
Errors<int>().bar(0);
|
||||
Errors<int>().bar(1); // expected-error{{bad i}}
|
||||
|
||||
Errors<int>().fooOvl(0);
|
||||
Errors<int>().fooOvl(1); // expected-error{{call to unavailable}}
|
||||
Errors<int>().fooOvl(short(0));
|
||||
Errors<int>().fooOvl(short(1)); // expected-error{{call to unavailable}}
|
||||
|
||||
Errors<int>().barOvl(0);
|
||||
Errors<int>().barOvl(1); // expected-error{{call to unavailable}}
|
||||
Errors<int>().barOvl(short(0));
|
||||
Errors<int>().barOvl(short(1)); // expected-error{{call to unavailable}}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct Warnings {
|
||||
void foo(int i) _diagnose_if(i, "bad i", "warning"); // expected-note{{from 'diagnose_if'}}
|
||||
void bar(int i) _diagnose_if(i != T(), "bad i", "warning"); // expected-note{{from 'diagnose_if'}}
|
||||
|
||||
void fooOvl(int i) _diagnose_if(i, "int bad i", "warning"); // expected-note{{from 'diagnose_if'}}
|
||||
void fooOvl(short i) _diagnose_if(i, "short bad i", "warning"); // expected-note{{from 'diagnose_if'}}
|
||||
|
||||
void barOvl(int i) _diagnose_if(i != T(), "int bad i", "warning"); // expected-note{{from 'diagnose_if'}}
|
||||
void barOvl(short i) _diagnose_if(i != T(), "short bad i", "warning"); // expected-note{{from 'diagnose_if'}}
|
||||
};
|
||||
|
||||
void runWarnings() {
|
||||
Warnings<int>().foo(0);
|
||||
Warnings<int>().foo(1); // expected-warning{{bad i}}
|
||||
|
||||
Warnings<int>().bar(0);
|
||||
Warnings<int>().bar(1); // expected-warning{{bad i}}
|
||||
|
||||
Warnings<int>().fooOvl(0);
|
||||
Warnings<int>().fooOvl(1); // expected-warning{{int bad i}}
|
||||
Warnings<int>().fooOvl(short(0));
|
||||
Warnings<int>().fooOvl(short(1)); // expected-warning{{short bad i}}
|
||||
|
||||
Warnings<int>().barOvl(0);
|
||||
Warnings<int>().barOvl(1); // expected-warning{{int bad i}}
|
||||
Warnings<int>().barOvl(short(0));
|
||||
Warnings<int>().barOvl(short(1)); // expected-warning{{short bad i}}
|
||||
}
|
||||
}
|
||||
|
||||
namespace template_specialization {
|
||||
template <typename T>
|
||||
struct Foo {
|
||||
void foo() _diagnose_if(1, "override me", "error"); // expected-note{{from 'diagnose_if'}}
|
||||
void bar(int i) _diagnose_if(i, "bad i", "error"); // expected-note{{from 'diagnose_if'}}
|
||||
void baz(int i);
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Foo<int> {
|
||||
void foo();
|
||||
void bar(int i);
|
||||
void baz(int i) _diagnose_if(i, "bad i", "error"); // expected-note{{from 'diagnose_if'}}
|
||||
};
|
||||
|
||||
void runAll() {
|
||||
Foo<double>().foo(); // expected-error{{override me}}
|
||||
Foo<int>().foo();
|
||||
|
||||
Foo<double>().bar(1); // expected-error{{bad i}}
|
||||
Foo<int>().bar(1);
|
||||
|
||||
Foo<double>().baz(1);
|
||||
Foo<int>().baz(1); // expected-error{{bad i}}
|
||||
}
|
||||
}
|
||||
|
||||
namespace late_constexpr {
|
||||
constexpr int foo();
|
||||
constexpr int foo(int a);
|
||||
|
||||
void bar() _diagnose_if(foo(), "bad foo", "error"); // expected-note{{from 'diagnose_if'}} expected-note{{not viable: requires 0 arguments}}
|
||||
void bar(int a) _diagnose_if(foo(a), "bad foo", "error"); // expected-note{{bad foo}}
|
||||
|
||||
void early() {
|
||||
bar();
|
||||
bar(0);
|
||||
bar(1);
|
||||
}
|
||||
|
||||
constexpr int foo() { return 1; }
|
||||
constexpr int foo(int a) { return a; }
|
||||
|
||||
void late() {
|
||||
bar(); // expected-error{{bad foo}}
|
||||
bar(0);
|
||||
bar(1); // expected-error{{call to unavailable function}}
|
||||
}
|
||||
}
|
||||
|
||||
namespace late_parsed {
|
||||
struct Foo {
|
||||
int i;
|
||||
constexpr Foo(int i): i(i) {}
|
||||
constexpr bool isFooable() const { return i; }
|
||||
|
||||
void go() const _diagnose_if(isFooable(), "oh no", "error") {} // expected-note{{from 'diagnose_if'}}
|
||||
operator int() const _diagnose_if(isFooable(), "oh no", "error") { return 1; } // expected-note{{oh no}}
|
||||
|
||||
void go2() const _diagnose_if(isFooable(), "oh no", "error") // expected-note{{oh no}}
|
||||
__attribute__((enable_if(true, ""))) {}
|
||||
void go2() const _diagnose_if(isFooable(), "oh no", "error") {} // expected-note{{oh no}}
|
||||
|
||||
constexpr int go3() const _diagnose_if(isFooable(), "oh no", "error")
|
||||
__attribute__((enable_if(true, ""))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
constexpr int go4() const _diagnose_if(isFooable(), "oh no", "error") {
|
||||
return 1;
|
||||
}
|
||||
constexpr int go4() const _diagnose_if(isFooable(), "oh no", "error")
|
||||
__attribute__((enable_if(true, ""))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// We hope to support emitting these errors in the future. For now, though...
|
||||
constexpr int runGo() const {
|
||||
return go3() + go4();
|
||||
}
|
||||
};
|
||||
|
||||
void go(const Foo &f) _diagnose_if(f.isFooable(), "oh no", "error") {} // expected-note{{oh no}}
|
||||
|
||||
void run() {
|
||||
Foo(0).go();
|
||||
Foo(1).go(); // expected-error{{oh no}}
|
||||
|
||||
(void)int(Foo(0));
|
||||
(void)int(Foo(1)); // expected-error{{uses deleted function}}
|
||||
|
||||
Foo(0).go2();
|
||||
Foo(1).go2(); // expected-error{{call to unavailable member function}}
|
||||
|
||||
go(Foo(0));
|
||||
go(Foo(1)); // expected-error{{call to unavailable function}}
|
||||
}
|
||||
}
|
||||
|
||||
namespace member_templates {
|
||||
struct Foo {
|
||||
int i;
|
||||
constexpr Foo(int i): i(i) {}
|
||||
constexpr bool bad() const { return i; }
|
||||
|
||||
template <typename T> T getVal() _diagnose_if(bad(), "oh no", "error") { // expected-note{{oh no}}
|
||||
return T();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T getVal2() const _diagnose_if(bad(), "oh no", "error") { // expected-note{{oh no}}
|
||||
return T();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr operator T() const _diagnose_if(bad(), "oh no", "error") { // expected-note{{oh no}}
|
||||
return T();
|
||||
}
|
||||
|
||||
// We hope to support emitting these errors in the future.
|
||||
int run() { return getVal<int>() + getVal2<int>() + int(*this); }
|
||||
};
|
||||
|
||||
void run() {
|
||||
Foo(0).getVal<int>();
|
||||
Foo(1).getVal<int>(); // expected-error{{call to unavailable member function}}
|
||||
|
||||
Foo(0).getVal2<int>();
|
||||
Foo(1).getVal2<int>(); // expected-error{{call to unavailable member function}}
|
||||
|
||||
(void)int(Foo(0));
|
||||
(void)int(Foo(1)); // expected-error{{uses deleted function}}
|
||||
}
|
||||
}
|
||||
|
||||
namespace special_member_operators {
|
||||
struct Bar { int j; };
|
||||
struct Foo {
|
||||
int i;
|
||||
constexpr Foo(int i): i(i) {}
|
||||
constexpr bool bad() const { return i; }
|
||||
const Bar *operator->() const _diagnose_if(bad(), "oh no", "error") { // expected-note{{oh no}}
|
||||
return nullptr;
|
||||
}
|
||||
void operator()() const _diagnose_if(bad(), "oh no", "error") {} // expected-note{{oh no}}
|
||||
};
|
||||
|
||||
struct ParenOverload {
|
||||
int i;
|
||||
constexpr ParenOverload(int i): i(i) {}
|
||||
constexpr bool bad() const { return i; }
|
||||
void operator()(double) const _diagnose_if(bad(), "oh no", "error") {} // expected-note 2{{oh no}}
|
||||
void operator()(int) const _diagnose_if(bad(), "oh no", "error") {} // expected-note 2{{oh no}}
|
||||
};
|
||||
|
||||
struct ParenTemplate {
|
||||
int i;
|
||||
constexpr ParenTemplate(int i): i(i) {}
|
||||
constexpr bool bad() const { return i; }
|
||||
template <typename T>
|
||||
void operator()(T) const _diagnose_if(bad(), "oh no", "error") {} // expected-note 2{{oh no}}
|
||||
};
|
||||
|
||||
void run() {
|
||||
(void)Foo(0)->j;
|
||||
(void)Foo(1)->j; // expected-error{{selected unavailable operator '->'}}
|
||||
|
||||
Foo(0)();
|
||||
Foo(1)(); // expected-error{{unavailable function call operator}}
|
||||
|
||||
ParenOverload(0)(1);
|
||||
ParenOverload(0)(1.);
|
||||
|
||||
ParenOverload(1)(1); // expected-error{{unavailable function call operator}}
|
||||
ParenOverload(1)(1.); // expected-error{{unavailable function call operator}}
|
||||
|
||||
ParenTemplate(0)(1);
|
||||
ParenTemplate(0)(1.);
|
||||
|
||||
ParenTemplate(1)(1); // expected-error{{unavailable function call operator}}
|
||||
ParenTemplate(1)(1.); // expected-error{{unavailable function call operator}}
|
||||
}
|
||||
|
||||
void runLambda() {
|
||||
auto L1 = [](int i) _diagnose_if(i, "oh no", "error") {}; // expected-note{{oh no}} expected-note{{conversion candidate}}
|
||||
L1(0);
|
||||
L1(1); // expected-error{{call to unavailable function call}}
|
||||
}
|
||||
}
|
||||
|
||||
namespace ctors {
|
||||
struct Foo {
|
||||
int I;
|
||||
constexpr Foo(int I): I(I) {}
|
||||
|
||||
constexpr const Foo &operator=(const Foo &) const // expected-note 2{{disabled: oh no}}
|
||||
_diagnose_if(I, "oh no", "error") {
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr const Foo &operator=(const Foo &&) const // expected-note{{disabled: oh no}} expected-note{{no known conversion}}
|
||||
_diagnose_if(I, "oh no", "error") {
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
void run() {
|
||||
constexpr Foo F{0};
|
||||
constexpr Foo F2{1};
|
||||
|
||||
F2 = F; // expected-error{{selected unavailable operator}}
|
||||
F2 = Foo{2}; // expected-error{{selected unavailable operator}}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user