Add the diagnose_if attribute to clang.
`diagnose_if` can be used to have clang emit either warnings or errors for function calls that meet user-specified conditions. For example: ``` constexpr int foo(int a) __attribute__((diagnose_if(a > 10, "configurations with a > 10 are " "expensive.", "warning"))); int f1 = foo(9); int f2 = foo(10); // warning: configuration with a > 10 are expensive. int f3 = foo(f2); ``` It currently only emits diagnostics in cases where the condition is guaranteed to always be true. So, the following code will emit no warnings: ``` constexpr int bar(int a) { foo(a); return 0; } constexpr int i = bar(10); ``` We hope to support optionally emitting diagnostics for cases like that (and emitting runtime checks) in the future. Release notes will appear shortly. :) Differential Revision: https://reviews.llvm.org/D27424 llvm-svn: 291418
This commit is contained in:
parent
ff567a8ba2
commit
177399e227
|
@ -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<
|
||||
|
@ -3369,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">;
|
||||
|
@ -4398,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>;
|
||||
|
|
|
@ -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>());
|
||||
|
|
|
@ -668,6 +668,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;
|
||||
|
||||
|
@ -732,17 +752,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 OverloadCandidate::Conversions 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;
|
||||
|
@ -751,12 +796,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) {
|
||||
|
@ -779,19 +829,7 @@ namespace clang {
|
|||
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);
|
||||
}
|
||||
|
||||
C.Conversions = slabAllocate<ImplicitConversionSequence>(NumConversions);
|
||||
// Construct the new objects.
|
||||
for (unsigned i = 0; i != NumConversions; ++i)
|
||||
new (&C.Conversions[i]) ImplicitConversionSequence();
|
||||
|
|
|
@ -2531,14 +2531,14 @@ 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);
|
||||
|
@ -2548,6 +2548,7 @@ public:
|
|||
TemplateArgumentListInfo *ExplicitTemplateArgs,
|
||||
QualType ObjectType,
|
||||
Expr::Classification ObjectClassification,
|
||||
Expr *ThisArg,
|
||||
ArrayRef<Expr *> Args,
|
||||
OverloadCandidateSet& CandidateSet,
|
||||
bool SuppressUserConversions = false,
|
||||
|
@ -2611,6 +2612,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.
|
||||
///
|
||||
|
|
|
@ -10410,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) {
|
||||
|
@ -10426,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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "clang/Sema/Template.h"
|
||||
#include "clang/Sema/TemplateDeduction.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
|
@ -830,12 +831,20 @@ void OverloadCandidateSet::destroyCandidates() {
|
|||
|
||||
void OverloadCandidateSet::clear() {
|
||||
destroyCandidates();
|
||||
ConversionSequenceAllocator.Reset();
|
||||
NumInlineSequences = 0;
|
||||
// DiagnoseIfAttrs are just pointers, so we don't need to destroy them.
|
||||
SlabAllocator.Reset();
|
||||
NumInlineBytesUsed = 0;
|
||||
Candidates.clear();
|
||||
Functions.clear();
|
||||
}
|
||||
|
||||
DiagnoseIfAttr **
|
||||
OverloadCandidateSet::addDiagnoseIfComplaints(ArrayRef<DiagnoseIfAttr *> CA) {
|
||||
auto *DIA = slabAllocate<DiagnoseIfAttr *>(CA.size());
|
||||
std::uninitialized_copy(CA.begin(), CA.end(), DIA);
|
||||
return DIA;
|
||||
}
|
||||
|
||||
namespace {
|
||||
class UnbridgedCastsSet {
|
||||
struct Entry {
|
||||
|
@ -5814,6 +5823,28 @@ static bool IsAcceptableNonMemberOperatorCandidate(ASTContext &Context,
|
|||
return false;
|
||||
}
|
||||
|
||||
static void initDiagnoseIfComplaint(Sema &S, OverloadCandidateSet &CandidateSet,
|
||||
OverloadCandidate &Candidate,
|
||||
FunctionDecl *Function,
|
||||
ArrayRef<Expr *> Args,
|
||||
bool MissingImplicitThis = false,
|
||||
Expr *ExplicitThis = nullptr) {
|
||||
SmallVector<DiagnoseIfAttr *, 8> Results;
|
||||
if (DiagnoseIfAttr *DIA = S.checkArgDependentDiagnoseIf(
|
||||
Function, Args, Results, MissingImplicitThis, ExplicitThis)) {
|
||||
Results.clear();
|
||||
Results.push_back(DIA);
|
||||
}
|
||||
|
||||
Candidate.NumTriggeredDiagnoseIfs = Results.size();
|
||||
if (Results.empty())
|
||||
Candidate.DiagnoseIfInfo = nullptr;
|
||||
else if (Results.size() == 1)
|
||||
Candidate.DiagnoseIfInfo = Results[0];
|
||||
else
|
||||
Candidate.DiagnoseIfInfo = CandidateSet.addDiagnoseIfComplaints(Results);
|
||||
}
|
||||
|
||||
/// AddOverloadCandidate - Adds the given function to the set of
|
||||
/// candidate functions, using the given function call arguments. If
|
||||
/// @p SuppressUserConversions, then don't allow user-defined
|
||||
|
@ -5847,8 +5878,8 @@ Sema::AddOverloadCandidate(FunctionDecl *Function,
|
|||
// is irrelevant.
|
||||
AddMethodCandidate(Method, FoundDecl, Method->getParent(),
|
||||
QualType(), Expr::Classification::makeSimpleLValue(),
|
||||
Args, CandidateSet, SuppressUserConversions,
|
||||
PartialOverloading);
|
||||
/*ThisArg=*/nullptr, Args, CandidateSet,
|
||||
SuppressUserConversions, PartialOverloading);
|
||||
return;
|
||||
}
|
||||
// We treat a constructor like a non-member function, since its object
|
||||
|
@ -6008,6 +6039,8 @@ Sema::AddOverloadCandidate(FunctionDecl *Function,
|
|||
Candidate.FailureKind = ovl_fail_ext_disabled;
|
||||
return;
|
||||
}
|
||||
|
||||
initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Function, Args);
|
||||
}
|
||||
|
||||
ObjCMethodDecl *
|
||||
|
@ -6120,66 +6153,87 @@ getOrderedEnableIfAttrs(const FunctionDecl *Function) {
|
|||
return Result;
|
||||
}
|
||||
|
||||
EnableIfAttr *Sema::CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
|
||||
bool MissingImplicitThis) {
|
||||
auto EnableIfAttrs = getOrderedEnableIfAttrs(Function);
|
||||
if (EnableIfAttrs.empty())
|
||||
return nullptr;
|
||||
|
||||
SFINAETrap Trap(*this);
|
||||
SmallVector<Expr *, 16> ConvertedArgs;
|
||||
bool InitializationFailed = false;
|
||||
static bool
|
||||
convertArgsForAvailabilityChecks(Sema &S, FunctionDecl *Function, Expr *ThisArg,
|
||||
ArrayRef<Expr *> Args, Sema::SFINAETrap &Trap,
|
||||
bool MissingImplicitThis, Expr *&ConvertedThis,
|
||||
SmallVectorImpl<Expr *> &ConvertedArgs) {
|
||||
if (ThisArg) {
|
||||
CXXMethodDecl *Method = cast<CXXMethodDecl>(Function);
|
||||
assert(!isa<CXXConstructorDecl>(Method) &&
|
||||
"Shouldn't have `this` for ctors!");
|
||||
assert(!Method->isStatic() && "Shouldn't have `this` for static methods!");
|
||||
ExprResult R = S.PerformObjectArgumentInitialization(
|
||||
ThisArg, /*Qualifier=*/nullptr, Method, Method);
|
||||
if (R.isInvalid())
|
||||
return false;
|
||||
ConvertedThis = R.get();
|
||||
} else {
|
||||
if (auto *MD = dyn_cast<CXXMethodDecl>(Function)) {
|
||||
(void)MD;
|
||||
assert((MissingImplicitThis || MD->isStatic() ||
|
||||
isa<CXXConstructorDecl>(MD)) &&
|
||||
"Expected `this` for non-ctor instance methods");
|
||||
}
|
||||
ConvertedThis = nullptr;
|
||||
}
|
||||
|
||||
// Ignore any variadic arguments. Converting them is pointless, since the
|
||||
// user can't refer to them in the enable_if condition.
|
||||
// user can't refer to them in the function condition.
|
||||
unsigned ArgSizeNoVarargs = std::min(Function->param_size(), Args.size());
|
||||
|
||||
// Convert the arguments.
|
||||
for (unsigned I = 0; I != ArgSizeNoVarargs; ++I) {
|
||||
ExprResult R;
|
||||
if (I == 0 && !MissingImplicitThis && isa<CXXMethodDecl>(Function) &&
|
||||
!cast<CXXMethodDecl>(Function)->isStatic() &&
|
||||
!isa<CXXConstructorDecl>(Function)) {
|
||||
CXXMethodDecl *Method = cast<CXXMethodDecl>(Function);
|
||||
R = PerformObjectArgumentInitialization(Args[0], /*Qualifier=*/nullptr,
|
||||
Method, Method);
|
||||
} else {
|
||||
R = PerformCopyInitialization(InitializedEntity::InitializeParameter(
|
||||
Context, Function->getParamDecl(I)),
|
||||
R = S.PerformCopyInitialization(InitializedEntity::InitializeParameter(
|
||||
S.Context, Function->getParamDecl(I)),
|
||||
SourceLocation(), Args[I]);
|
||||
}
|
||||
|
||||
if (R.isInvalid()) {
|
||||
InitializationFailed = true;
|
||||
break;
|
||||
}
|
||||
if (R.isInvalid())
|
||||
return false;
|
||||
|
||||
ConvertedArgs.push_back(R.get());
|
||||
}
|
||||
|
||||
if (InitializationFailed || Trap.hasErrorOccurred())
|
||||
return EnableIfAttrs[0];
|
||||
if (Trap.hasErrorOccurred())
|
||||
return false;
|
||||
|
||||
// Push default arguments if needed.
|
||||
if (!Function->isVariadic() && Args.size() < Function->getNumParams()) {
|
||||
for (unsigned i = Args.size(), e = Function->getNumParams(); i != e; ++i) {
|
||||
ParmVarDecl *P = Function->getParamDecl(i);
|
||||
ExprResult R = PerformCopyInitialization(
|
||||
InitializedEntity::InitializeParameter(Context,
|
||||
ExprResult R = S.PerformCopyInitialization(
|
||||
InitializedEntity::InitializeParameter(S.Context,
|
||||
Function->getParamDecl(i)),
|
||||
SourceLocation(),
|
||||
P->hasUninstantiatedDefaultArg() ? P->getUninstantiatedDefaultArg()
|
||||
: P->getDefaultArg());
|
||||
if (R.isInvalid()) {
|
||||
InitializationFailed = true;
|
||||
break;
|
||||
}
|
||||
if (R.isInvalid())
|
||||
return false;
|
||||
ConvertedArgs.push_back(R.get());
|
||||
}
|
||||
|
||||
if (InitializationFailed || Trap.hasErrorOccurred())
|
||||
return EnableIfAttrs[0];
|
||||
if (Trap.hasErrorOccurred())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
EnableIfAttr *Sema::CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
|
||||
bool MissingImplicitThis) {
|
||||
SmallVector<EnableIfAttr *, 4> EnableIfAttrs =
|
||||
getOrderedEnableIfAttrs(Function);
|
||||
if (EnableIfAttrs.empty())
|
||||
return nullptr;
|
||||
|
||||
SFINAETrap Trap(*this);
|
||||
SmallVector<Expr *, 16> ConvertedArgs;
|
||||
// FIXME: We should look into making enable_if late-parsed.
|
||||
Expr *DiscardedThis;
|
||||
if (!convertArgsForAvailabilityChecks(
|
||||
*this, Function, /*ThisArg=*/nullptr, Args, Trap,
|
||||
/*MissingImplicitThis=*/true, DiscardedThis, ConvertedArgs))
|
||||
return EnableIfAttrs[0];
|
||||
|
||||
for (auto *EIA : EnableIfAttrs) {
|
||||
APValue Result;
|
||||
|
@ -6195,6 +6249,87 @@ EnableIfAttr *Sema::CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
static bool gatherDiagnoseIfAttrs(FunctionDecl *Function, bool ArgDependent,
|
||||
SmallVectorImpl<DiagnoseIfAttr *> &Errors,
|
||||
SmallVectorImpl<DiagnoseIfAttr *> &Nonfatal) {
|
||||
for (auto *DIA : Function->specific_attrs<DiagnoseIfAttr>())
|
||||
if (ArgDependent == DIA->getArgDependent()) {
|
||||
if (DIA->isError())
|
||||
Errors.push_back(DIA);
|
||||
else
|
||||
Nonfatal.push_back(DIA);
|
||||
}
|
||||
|
||||
return !Errors.empty() || !Nonfatal.empty();
|
||||
}
|
||||
|
||||
template <typename CheckFn>
|
||||
static DiagnoseIfAttr *
|
||||
checkDiagnoseIfAttrsWith(const SmallVectorImpl<DiagnoseIfAttr *> &Errors,
|
||||
SmallVectorImpl<DiagnoseIfAttr *> &Nonfatal,
|
||||
CheckFn &&IsSuccessful) {
|
||||
// Note that diagnose_if attributes are late-parsed, so they appear in the
|
||||
// correct order (unlike enable_if attributes).
|
||||
auto ErrAttr = llvm::find_if(Errors, IsSuccessful);
|
||||
if (ErrAttr != Errors.end())
|
||||
return *ErrAttr;
|
||||
|
||||
llvm::erase_if(Nonfatal, [&](DiagnoseIfAttr *A) { return !IsSuccessful(A); });
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DiagnoseIfAttr *
|
||||
Sema::checkArgDependentDiagnoseIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
|
||||
SmallVectorImpl<DiagnoseIfAttr *> &Nonfatal,
|
||||
bool MissingImplicitThis,
|
||||
Expr *ThisArg) {
|
||||
SmallVector<DiagnoseIfAttr *, 4> Errors;
|
||||
if (!gatherDiagnoseIfAttrs(Function, /*ArgDependent=*/true, Errors, Nonfatal))
|
||||
return nullptr;
|
||||
|
||||
SFINAETrap Trap(*this);
|
||||
SmallVector<Expr *, 16> ConvertedArgs;
|
||||
Expr *ConvertedThis;
|
||||
if (!convertArgsForAvailabilityChecks(*this, Function, ThisArg, Args, Trap,
|
||||
MissingImplicitThis, ConvertedThis,
|
||||
ConvertedArgs))
|
||||
return nullptr;
|
||||
|
||||
return checkDiagnoseIfAttrsWith(Errors, Nonfatal, [&](DiagnoseIfAttr *DIA) {
|
||||
APValue Result;
|
||||
// It's sane to use the same ConvertedArgs for any redecl of this function,
|
||||
// since EvaluateWithSubstitution only cares about the position of each
|
||||
// argument in the arg list, not the ParmVarDecl* it maps to.
|
||||
if (!DIA->getCond()->EvaluateWithSubstitution(
|
||||
Result, Context, DIA->getParent(), ConvertedArgs, ConvertedThis))
|
||||
return false;
|
||||
return Result.isInt() && Result.getInt().getBoolValue();
|
||||
});
|
||||
}
|
||||
|
||||
DiagnoseIfAttr *Sema::checkArgIndependentDiagnoseIf(
|
||||
FunctionDecl *Function, SmallVectorImpl<DiagnoseIfAttr *> &Nonfatal) {
|
||||
SmallVector<DiagnoseIfAttr *, 4> Errors;
|
||||
if (!gatherDiagnoseIfAttrs(Function, /*ArgDependent=*/false, Errors,
|
||||
Nonfatal))
|
||||
return nullptr;
|
||||
|
||||
return checkDiagnoseIfAttrsWith(Errors, Nonfatal, [&](DiagnoseIfAttr *DIA) {
|
||||
bool Result;
|
||||
return DIA->getCond()->EvaluateAsBooleanCondition(Result, Context) &&
|
||||
Result;
|
||||
});
|
||||
}
|
||||
|
||||
void Sema::emitDiagnoseIfDiagnostic(SourceLocation Loc,
|
||||
const DiagnoseIfAttr *DIA) {
|
||||
auto Code = DIA->isError() ? diag::err_diagnose_if_succeeded
|
||||
: diag::warn_diagnose_if_succeeded;
|
||||
Diag(Loc, Code) << DIA->getMessage();
|
||||
Diag(DIA->getLocation(), diag::note_from_diagnose_if)
|
||||
<< DIA->getParent() << DIA->getCond()->getSourceRange();
|
||||
}
|
||||
|
||||
/// \brief Add all of the function declarations in the given function set to
|
||||
/// the overload candidate set.
|
||||
void Sema::AddFunctionCandidates(const UnresolvedSetImpl &Fns,
|
||||
|
@ -6210,7 +6345,7 @@ void Sema::AddFunctionCandidates(const UnresolvedSetImpl &Fns,
|
|||
AddMethodCandidate(cast<CXXMethodDecl>(FD), F.getPair(),
|
||||
cast<CXXMethodDecl>(FD)->getParent(),
|
||||
Args[0]->getType(), Args[0]->Classify(Context),
|
||||
Args.slice(1), CandidateSet,
|
||||
Args[0], Args.slice(1), CandidateSet,
|
||||
SuppressUserConversions, PartialOverloading);
|
||||
else
|
||||
AddOverloadCandidate(FD, F.getPair(), Args, CandidateSet,
|
||||
|
@ -6219,13 +6354,12 @@ void Sema::AddFunctionCandidates(const UnresolvedSetImpl &Fns,
|
|||
FunctionTemplateDecl *FunTmpl = cast<FunctionTemplateDecl>(D);
|
||||
if (isa<CXXMethodDecl>(FunTmpl->getTemplatedDecl()) &&
|
||||
!cast<CXXMethodDecl>(FunTmpl->getTemplatedDecl())->isStatic())
|
||||
AddMethodTemplateCandidate(FunTmpl, F.getPair(),
|
||||
cast<CXXRecordDecl>(FunTmpl->getDeclContext()),
|
||||
ExplicitTemplateArgs,
|
||||
Args[0]->getType(),
|
||||
Args[0]->Classify(Context), Args.slice(1),
|
||||
CandidateSet, SuppressUserConversions,
|
||||
PartialOverloading);
|
||||
AddMethodTemplateCandidate(
|
||||
FunTmpl, F.getPair(),
|
||||
cast<CXXRecordDecl>(FunTmpl->getDeclContext()),
|
||||
ExplicitTemplateArgs, Args[0]->getType(),
|
||||
Args[0]->Classify(Context), Args[0], Args.slice(1), CandidateSet,
|
||||
SuppressUserConversions, PartialOverloading);
|
||||
else
|
||||
AddTemplateOverloadCandidate(FunTmpl, F.getPair(),
|
||||
ExplicitTemplateArgs, Args,
|
||||
|
@ -6240,6 +6374,7 @@ void Sema::AddFunctionCandidates(const UnresolvedSetImpl &Fns,
|
|||
void Sema::AddMethodCandidate(DeclAccessPair FoundDecl,
|
||||
QualType ObjectType,
|
||||
Expr::Classification ObjectClassification,
|
||||
Expr *ThisArg,
|
||||
ArrayRef<Expr *> Args,
|
||||
OverloadCandidateSet& CandidateSet,
|
||||
bool SuppressUserConversions) {
|
||||
|
@ -6255,12 +6390,12 @@ void Sema::AddMethodCandidate(DeclAccessPair FoundDecl,
|
|||
AddMethodTemplateCandidate(TD, FoundDecl, ActingContext,
|
||||
/*ExplicitArgs*/ nullptr,
|
||||
ObjectType, ObjectClassification,
|
||||
Args, CandidateSet,
|
||||
ThisArg, Args, CandidateSet,
|
||||
SuppressUserConversions);
|
||||
} else {
|
||||
AddMethodCandidate(cast<CXXMethodDecl>(Decl), FoundDecl, ActingContext,
|
||||
ObjectType, ObjectClassification,
|
||||
Args,
|
||||
ThisArg, Args,
|
||||
CandidateSet, SuppressUserConversions);
|
||||
}
|
||||
}
|
||||
|
@ -6276,7 +6411,7 @@ void
|
|||
Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
|
||||
CXXRecordDecl *ActingContext, QualType ObjectType,
|
||||
Expr::Classification ObjectClassification,
|
||||
ArrayRef<Expr *> Args,
|
||||
Expr *ThisArg, ArrayRef<Expr *> Args,
|
||||
OverloadCandidateSet &CandidateSet,
|
||||
bool SuppressUserConversions,
|
||||
bool PartialOverloading) {
|
||||
|
@ -6393,6 +6528,9 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
|
|||
Candidate.DeductionFailure.Data = FailedAttr;
|
||||
return;
|
||||
}
|
||||
|
||||
initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Method, Args,
|
||||
/*MissingImplicitThis=*/!ThisArg, ThisArg);
|
||||
}
|
||||
|
||||
/// \brief Add a C++ member function template as a candidate to the candidate
|
||||
|
@ -6405,6 +6543,7 @@ Sema::AddMethodTemplateCandidate(FunctionTemplateDecl *MethodTmpl,
|
|||
TemplateArgumentListInfo *ExplicitTemplateArgs,
|
||||
QualType ObjectType,
|
||||
Expr::Classification ObjectClassification,
|
||||
Expr *ThisArg,
|
||||
ArrayRef<Expr *> Args,
|
||||
OverloadCandidateSet& CandidateSet,
|
||||
bool SuppressUserConversions,
|
||||
|
@ -6445,8 +6584,9 @@ Sema::AddMethodTemplateCandidate(FunctionTemplateDecl *MethodTmpl,
|
|||
assert(isa<CXXMethodDecl>(Specialization) &&
|
||||
"Specialization is not a member function?");
|
||||
AddMethodCandidate(cast<CXXMethodDecl>(Specialization), FoundDecl,
|
||||
ActingContext, ObjectType, ObjectClassification, Args,
|
||||
CandidateSet, SuppressUserConversions, PartialOverloading);
|
||||
ActingContext, ObjectType, ObjectClassification,
|
||||
/*ThisArg=*/ThisArg, Args, CandidateSet,
|
||||
SuppressUserConversions, PartialOverloading);
|
||||
}
|
||||
|
||||
/// \brief Add a C++ function template specialization as a candidate
|
||||
|
@ -6702,6 +6842,8 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion,
|
|||
Candidate.DeductionFailure.Data = FailedAttr;
|
||||
return;
|
||||
}
|
||||
|
||||
initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Conversion, None, false, From);
|
||||
}
|
||||
|
||||
/// \brief Adds a conversion function template specialization
|
||||
|
@ -6854,6 +6996,8 @@ void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion,
|
|||
Candidate.DeductionFailure.Data = FailedAttr;
|
||||
return;
|
||||
}
|
||||
|
||||
initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Conversion, None);
|
||||
}
|
||||
|
||||
/// \brief Add overload candidates for overloaded operators that are
|
||||
|
@ -6902,10 +7046,8 @@ void Sema::AddMemberOperatorCandidates(OverloadedOperatorKind Op,
|
|||
Oper != OperEnd;
|
||||
++Oper)
|
||||
AddMethodCandidate(Oper.getPair(), Args[0]->getType(),
|
||||
Args[0]->Classify(Context),
|
||||
Args.slice(1),
|
||||
CandidateSet,
|
||||
/* SuppressUserConversions = */ false);
|
||||
Args[0]->Classify(Context), Args[0], Args.slice(1),
|
||||
CandidateSet, /*SuppressUserConversions=*/false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8936,6 +9078,17 @@ void Sema::diagnoseEquivalentInternalLinkageDeclarations(
|
|||
}
|
||||
}
|
||||
|
||||
static bool isCandidateUnavailableDueToDiagnoseIf(const OverloadCandidate &OC) {
|
||||
ArrayRef<DiagnoseIfAttr *> Info = OC.getDiagnoseIfInfo();
|
||||
if (!Info.empty() && Info[0]->isError())
|
||||
return true;
|
||||
|
||||
assert(llvm::all_of(Info,
|
||||
[](const DiagnoseIfAttr *A) { return !A->isError(); }) &&
|
||||
"DiagnoseIf info shouldn't have mixed warnings and errors.");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Computes the best viable function (C++ 13.3.3)
|
||||
/// within an overload candidate set.
|
||||
///
|
||||
|
@ -9014,13 +9167,19 @@ OverloadCandidateSet::BestViableFunction(Sema &S, SourceLocation Loc,
|
|||
// Best is the best viable function.
|
||||
if (Best->Function &&
|
||||
(Best->Function->isDeleted() ||
|
||||
S.isFunctionConsideredUnavailable(Best->Function)))
|
||||
S.isFunctionConsideredUnavailable(Best->Function) ||
|
||||
isCandidateUnavailableDueToDiagnoseIf(*Best)))
|
||||
return OR_Deleted;
|
||||
|
||||
if (!EquivalentCands.empty())
|
||||
S.diagnoseEquivalentInternalLinkageDeclarations(Loc, Best->Function,
|
||||
EquivalentCands);
|
||||
|
||||
for (const auto *W : Best->getDiagnoseIfInfo()) {
|
||||
assert(W->isWarning() && "Errors should've been caught earlier!");
|
||||
S.emitDiagnoseIfDiagnostic(Loc, W);
|
||||
}
|
||||
|
||||
return OR_Success;
|
||||
}
|
||||
|
||||
|
@ -9861,7 +10020,7 @@ static void DiagnoseFailedEnableIfAttr(Sema &S, OverloadCandidate *Cand) {
|
|||
EnableIfAttr *Attr = static_cast<EnableIfAttr*>(Cand->DeductionFailure.Data);
|
||||
|
||||
S.Diag(Callee->getLocation(),
|
||||
diag::note_ovl_candidate_disabled_by_enable_if_attr)
|
||||
diag::note_ovl_candidate_disabled_by_function_cond_attr)
|
||||
<< Attr->getCond()->getSourceRange() << Attr->getMessage();
|
||||
}
|
||||
|
||||
|
@ -9891,21 +10050,28 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand,
|
|||
FunctionDecl *Fn = Cand->Function;
|
||||
|
||||
// Note deleted candidates, but only if they're viable.
|
||||
if (Cand->Viable && (Fn->isDeleted() ||
|
||||
S.isFunctionConsideredUnavailable(Fn))) {
|
||||
std::string FnDesc;
|
||||
OverloadCandidateKind FnKind =
|
||||
if (Cand->Viable) {
|
||||
if (Fn->isDeleted() || S.isFunctionConsideredUnavailable(Fn)) {
|
||||
std::string FnDesc;
|
||||
OverloadCandidateKind FnKind =
|
||||
ClassifyOverloadCandidate(S, Cand->FoundDecl, Fn, FnDesc);
|
||||
|
||||
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_deleted)
|
||||
<< FnKind << FnDesc
|
||||
<< (Fn->isDeleted() ? (Fn->isDeletedAsWritten() ? 1 : 2) : 0);
|
||||
MaybeEmitInheritedConstructorNote(S, Cand->FoundDecl);
|
||||
return;
|
||||
}
|
||||
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_deleted)
|
||||
<< FnKind << FnDesc
|
||||
<< (Fn->isDeleted() ? (Fn->isDeletedAsWritten() ? 1 : 2) : 0);
|
||||
MaybeEmitInheritedConstructorNote(S, Cand->FoundDecl);
|
||||
return;
|
||||
}
|
||||
if (isCandidateUnavailableDueToDiagnoseIf(*Cand)) {
|
||||
auto *A = Cand->DiagnoseIfInfo.get<DiagnoseIfAttr *>();
|
||||
assert(A->isError() && "Non-error diagnose_if disables a candidate?");
|
||||
S.Diag(Cand->Function->getLocation(),
|
||||
diag::note_ovl_candidate_disabled_by_function_cond_attr)
|
||||
<< A->getCond()->getSourceRange() << A->getMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't really have anything else to say about viable candidates.
|
||||
if (Cand->Viable) {
|
||||
// We don't really have anything else to say about viable candidates.
|
||||
S.NoteOverloadCandidate(Cand->FoundDecl, Fn);
|
||||
return;
|
||||
}
|
||||
|
@ -12460,6 +12626,16 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
|
|||
TemplateArgs = &TemplateArgsBuffer;
|
||||
}
|
||||
|
||||
// Poor-programmer's Lazy<Expr *>; isImplicitAccess requires stripping
|
||||
// parens/casts, which would be nice to avoid potentially doing multiple
|
||||
// times.
|
||||
llvm::Optional<Expr *> UnresolvedBase;
|
||||
auto GetUnresolvedBase = [&] {
|
||||
if (!UnresolvedBase.hasValue())
|
||||
UnresolvedBase =
|
||||
UnresExpr->isImplicitAccess() ? nullptr : UnresExpr->getBase();
|
||||
return *UnresolvedBase;
|
||||
};
|
||||
for (UnresolvedMemberExpr::decls_iterator I = UnresExpr->decls_begin(),
|
||||
E = UnresExpr->decls_end(); I != E; ++I) {
|
||||
|
||||
|
@ -12480,14 +12656,15 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
|
|||
continue;
|
||||
|
||||
AddMethodCandidate(Method, I.getPair(), ActingDC, ObjectType,
|
||||
ObjectClassification, Args, CandidateSet,
|
||||
ObjectClassification,
|
||||
/*ThisArg=*/GetUnresolvedBase(), Args, CandidateSet,
|
||||
/*SuppressUserConversions=*/false);
|
||||
} else {
|
||||
AddMethodTemplateCandidate(cast<FunctionTemplateDecl>(Func),
|
||||
I.getPair(), ActingDC, TemplateArgs,
|
||||
ObjectType, ObjectClassification,
|
||||
Args, CandidateSet,
|
||||
/*SuppressUsedConversions=*/false);
|
||||
AddMethodTemplateCandidate(
|
||||
cast<FunctionTemplateDecl>(Func), I.getPair(), ActingDC,
|
||||
TemplateArgs, ObjectType, ObjectClassification,
|
||||
/*ThisArg=*/GetUnresolvedBase(), Args, CandidateSet,
|
||||
/*SuppressUsedConversions=*/false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12600,10 +12777,20 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
|
|||
diag::err_ovl_no_viable_member_function_in_call)
|
||||
<< Method << Method->getSourceRange();
|
||||
Diag(Method->getLocation(),
|
||||
diag::note_ovl_candidate_disabled_by_enable_if_attr)
|
||||
diag::note_ovl_candidate_disabled_by_function_cond_attr)
|
||||
<< Attr->getCond()->getSourceRange() << Attr->getMessage();
|
||||
return ExprError();
|
||||
}
|
||||
|
||||
SmallVector<DiagnoseIfAttr *, 4> Nonfatal;
|
||||
if (const DiagnoseIfAttr *Attr = checkArgDependentDiagnoseIf(
|
||||
Method, Args, Nonfatal, false, MemE->getBase())) {
|
||||
emitDiagnoseIfDiagnostic(MemE->getMemberLoc(), Attr);
|
||||
return ExprError();
|
||||
}
|
||||
|
||||
for (const auto *Attr : Nonfatal)
|
||||
emitDiagnoseIfDiagnostic(MemE->getMemberLoc(), Attr);
|
||||
}
|
||||
|
||||
if ((isa<CXXConstructorDecl>(CurContext) ||
|
||||
|
@ -12683,7 +12870,7 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
|
|||
Oper != OperEnd; ++Oper) {
|
||||
AddMethodCandidate(Oper.getPair(), Object.get()->getType(),
|
||||
Object.get()->Classify(Context),
|
||||
Args, CandidateSet,
|
||||
Object.get(), Args, CandidateSet,
|
||||
/*SuppressUserConversions=*/ false);
|
||||
}
|
||||
|
||||
|
@ -12959,7 +13146,8 @@ Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc,
|
|||
for (LookupResult::iterator Oper = R.begin(), OperEnd = R.end();
|
||||
Oper != OperEnd; ++Oper) {
|
||||
AddMethodCandidate(Oper.getPair(), Base->getType(), Base->Classify(Context),
|
||||
None, CandidateSet, /*SuppressUserConversions=*/false);
|
||||
Base, None, CandidateSet,
|
||||
/*SuppressUserConversions=*/false);
|
||||
}
|
||||
|
||||
bool HadMultipleCandidates = (CandidateSet.size() > 1);
|
||||
|
|
|
@ -168,40 +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;
|
||||
{
|
||||
Sema::ContextRAII SwitchContext(S, cast<FunctionDecl>(New));
|
||||
Sema::ContextRAII SwitchContext(S, New);
|
||||
EnterExpressionEvaluationContext Unevaluated(S, Sema::ConstantEvaluated);
|
||||
ExprResult Result = S.SubstExpr(A->getCond(), TemplateArgs);
|
||||
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
|
||||
|
@ -335,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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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}}
|
||||
}
|
|
@ -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}}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue