/// 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.
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>;
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>;
}];
}
+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 = [{
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<
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">;
OverloadedVirtual,
PrivateExtern,
SelTypeCast,
- ExternCCompat
+ ExternCCompat,
+ UserDefinedWarnings
]>;
// Thread Safety warnings
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<
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">;
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>;
/// \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>());
/// 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;
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;
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) {
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();
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);
TemplateArgumentListInfo *ExplicitTemplateArgs,
QualType ObjectType,
Expr::Classification ObjectClassification,
+ Expr *ThisArg,
ArrayRef<Expr *> Args,
OverloadCandidateSet& CandidateSet,
bool SuppressUserConversions = false,
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.
///
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) {
}
// 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;
}
#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"
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,
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;
}
// 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);
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
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);
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.
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,
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,
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,
#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"
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 {
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
// 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
Candidate.FailureKind = ovl_fail_ext_disabled;
return;
}
+
+ initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Function, Args);
}
ObjCMethodDecl *
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;
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,
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,
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,
void Sema::AddMethodCandidate(DeclAccessPair FoundDecl,
QualType ObjectType,
Expr::Classification ObjectClassification,
+ Expr *ThisArg,
ArrayRef<Expr *> Args,
OverloadCandidateSet& CandidateSet,
bool SuppressUserConversions) {
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);
}
}
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) {
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
TemplateArgumentListInfo *ExplicitTemplateArgs,
QualType ObjectType,
Expr::Classification ObjectClassification,
+ Expr *ThisArg,
ArrayRef<Expr *> Args,
OverloadCandidateSet& CandidateSet,
bool SuppressUserConversions,
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
Candidate.DeductionFailure.Data = FailedAttr;
return;
}
+
+ initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Conversion, None, false, From);
}
/// \brief Adds a conversion function template specialization
Candidate.DeductionFailure.Data = FailedAttr;
return;
}
+
+ initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Conversion, None);
}
/// \brief Add overload candidates for overloaded operators that are
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);
}
}
}
}
+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.
///
// 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;
}
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();
}
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;
}
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) {
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);
}
}
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) ||
Oper != OperEnd; ++Oper) {
AddMethodCandidate(Oper.getPair(), Object.get()->getType(),
Object.get()->Classify(Context),
- Args, CandidateSet,
+ Object.get(), Args, CandidateSet,
/*SuppressUserConversions=*/ false);
}
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);
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
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;
}
--- /dev/null
+// 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}}
+}
--- /dev/null
+// 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}}
+}
+}