summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/clang/Sema/Overload.h32
-rw-r--r--include/clang/Sema/Sema.h51
-rw-r--r--lib/Sema/SemaChecking.cpp34
-rw-r--r--lib/Sema/SemaExpr.cpp19
-rw-r--r--lib/Sema/SemaExprCXX.cpp5
-rw-r--r--lib/Sema/SemaLookup.cpp3
-rw-r--r--lib/Sema/SemaOverload.cpp285
-rw-r--r--test/Sema/diagnose_if.c6
-rw-r--r--test/SemaCXX/diagnose_if.cpp313
9 files changed, 415 insertions, 333 deletions
diff --git a/include/clang/Sema/Overload.h b/include/clang/Sema/Overload.h
index 88fdc991f3..5220d98730 100644
--- a/include/clang/Sema/Overload.h
+++ b/include/clang/Sema/Overload.h
@@ -675,26 +675,6 @@ 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;
@@ -759,9 +739,8 @@ namespace clang {
SmallVector<OverloadCandidate, 16> Candidates;
llvm::SmallPtrSet<Decl *, 16> Functions;
- // Allocator for ConversionSequenceLists and DiagnoseIfAttr* arrays.
- // We store the first few of each of these inline to avoid allocation for
- // small sets.
+ // Allocator for ConversionSequenceLists. We store the first few of these
+ // inline to avoid allocation for small sets.
llvm::BumpPtrAllocator SlabAllocator;
SourceLocation Loc;
@@ -776,6 +755,8 @@ namespace clang {
/// from the slab allocator.
/// FIXME: It would probably be nice to have a SmallBumpPtrAllocator
/// instead.
+ /// FIXME: Now that this only allocates ImplicitConversionSequences, do we
+ /// want to un-generalize this?
template <typename T>
T *slabAllocate(unsigned N) {
// It's simpler if this doesn't need to consider alignment.
@@ -809,11 +790,6 @@ namespace clang {
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) {
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index c180a8ea3e..63d078498f 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -2532,14 +2532,14 @@ public:
void AddMethodCandidate(DeclAccessPair FoundDecl,
QualType ObjectType,
Expr::Classification ObjectClassification,
- Expr *ThisArg, ArrayRef<Expr *> Args,
+ ArrayRef<Expr *> Args,
OverloadCandidateSet& CandidateSet,
bool SuppressUserConversion = false);
void AddMethodCandidate(CXXMethodDecl *Method,
DeclAccessPair FoundDecl,
CXXRecordDecl *ActingContext, QualType ObjectType,
Expr::Classification ObjectClassification,
- Expr *ThisArg, ArrayRef<Expr *> Args,
+ ArrayRef<Expr *> Args,
OverloadCandidateSet& CandidateSet,
bool SuppressUserConversions = false,
bool PartialOverloading = false,
@@ -2550,7 +2550,6 @@ public:
TemplateArgumentListInfo *ExplicitTemplateArgs,
QualType ObjectType,
Expr::Classification ObjectClassification,
- Expr *ThisArg,
ArrayRef<Expr *> Args,
OverloadCandidateSet& CandidateSet,
bool SuppressUserConversions = false,
@@ -2624,37 +2623,27 @@ 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.
+ /// Emit diagnostics for the diagnose_if attributes on Function, ignoring any
+ /// non-ArgDependent DiagnoseIfAttrs.
///
- /// This only considers ArgDependent DiagnoseIfAttrs.
+ /// Argument-dependent diagnose_if attributes should be checked each time a
+ /// function is used as a direct callee of a function call.
///
- /// 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);
+ /// Returns true if any errors were emitted.
+ bool diagnoseArgDependentDiagnoseIfAttrs(const FunctionDecl *Function,
+ const Expr *ThisArg,
+ ArrayRef<const Expr *> Args,
+ SourceLocation Loc);
- /// Check the diagnose_if expressions on the given function. Returns the
- /// first succesful fatal attribute, or null if using Function isn't
- /// an error.
+ /// Emit diagnostics for the diagnose_if attributes on Function, ignoring any
+ /// ArgDependent DiagnoseIfAttrs.
///
- /// This ignores all ArgDependent DiagnoseIfAttrs.
+ /// Argument-independent diagnose_if attributes should be checked on every use
+ /// of a function.
///
- /// 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 true if any errors were emitted.
+ bool diagnoseArgIndependentDiagnoseIfAttrs(const FunctionDecl *Function,
+ SourceLocation Loc);
/// Returns whether the given function's address can be taken or not,
/// optionally emitting a diagnostic if the address can't be taken.
@@ -9914,8 +9903,8 @@ private:
SourceLocation Loc);
void checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
- ArrayRef<const Expr *> Args, bool IsMemberFunction,
- SourceLocation Loc, SourceRange Range,
+ const Expr *ThisArg, ArrayRef<const Expr *> Args,
+ bool IsMemberFunction, SourceLocation Loc, SourceRange Range,
VariadicCallType CallType);
bool CheckObjCString(Expr *Arg);
diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp
index 49208e20a4..3aedb2a8c9 100644
--- a/lib/Sema/SemaChecking.cpp
+++ b/lib/Sema/SemaChecking.cpp
@@ -2426,11 +2426,12 @@ static void CheckNonNullArguments(Sema &S,
}
/// Handles the checks for format strings, non-POD arguments to vararg
-/// functions, and NULL arguments passed to non-NULL parameters.
+/// functions, NULL arguments passed to non-NULL parameters, and diagnose_if
+/// attributes.
void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
- ArrayRef<const Expr *> Args, bool IsMemberFunction,
- SourceLocation Loc, SourceRange Range,
- VariadicCallType CallType) {
+ const Expr *ThisArg, ArrayRef<const Expr *> Args,
+ bool IsMemberFunction, SourceLocation Loc,
+ SourceRange Range, VariadicCallType CallType) {
// FIXME: We should check as much as we can in the template definition.
if (CurContext->isDependentContext())
return;
@@ -2477,6 +2478,9 @@ void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
CheckArgumentWithTypeTag(I, Args.data());
}
}
+
+ if (FD)
+ diagnoseArgDependentDiagnoseIfAttrs(FD, ThisArg, Args, Loc);
}
/// CheckConstructorCall - Check a constructor call for correctness and safety
@@ -2487,8 +2491,8 @@ void Sema::CheckConstructorCall(FunctionDecl *FDecl,
SourceLocation Loc) {
VariadicCallType CallType =
Proto->isVariadic() ? VariadicConstructor : VariadicDoesNotApply;
- checkCall(FDecl, Proto, Args, /*IsMemberFunction=*/true, Loc, SourceRange(),
- CallType);
+ checkCall(FDecl, Proto, /*ThisArg=*/nullptr, Args, /*IsMemberFunction=*/true,
+ Loc, SourceRange(), CallType);
}
/// CheckFunctionCall - Check a direct function call for various correctness
@@ -2503,14 +2507,20 @@ bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall,
TheCall->getCallee());
Expr** Args = TheCall->getArgs();
unsigned NumArgs = TheCall->getNumArgs();
+
+ Expr *ImplicitThis = nullptr;
if (IsMemberOperatorCall) {
// If this is a call to a member operator, hide the first argument
// from checkCall.
// FIXME: Our choice of AST representation here is less than ideal.
+ ImplicitThis = Args[0];
++Args;
--NumArgs;
- }
- checkCall(FDecl, Proto, llvm::makeArrayRef(Args, NumArgs),
+ } else if (IsMemberFunction)
+ ImplicitThis =
+ cast<CXXMemberCallExpr>(TheCall)->getImplicitObjectArgument();
+
+ checkCall(FDecl, Proto, ImplicitThis, llvm::makeArrayRef(Args, NumArgs),
IsMemberFunction, TheCall->getRParenLoc(),
TheCall->getCallee()->getSourceRange(), CallType);
@@ -2546,8 +2556,8 @@ bool Sema::CheckObjCMethodCall(ObjCMethodDecl *Method, SourceLocation lbrac,
VariadicCallType CallType =
Method->isVariadic() ? VariadicMethod : VariadicDoesNotApply;
- checkCall(Method, nullptr, Args,
- /*IsMemberFunction=*/false, lbrac, Method->getSourceRange(),
+ checkCall(Method, nullptr, /*ThisArg=*/nullptr, Args,
+ /*IsMemberFunction=*/false, lbrac, Method->getSourceRange(),
CallType);
return false;
@@ -2576,7 +2586,7 @@ bool Sema::CheckPointerCall(NamedDecl *NDecl, CallExpr *TheCall,
CallType = VariadicFunction;
}
- checkCall(NDecl, Proto,
+ checkCall(NDecl, Proto, /*ThisArg=*/nullptr,
llvm::makeArrayRef(TheCall->getArgs(), TheCall->getNumArgs()),
/*IsMemberFunction=*/false, TheCall->getRParenLoc(),
TheCall->getCallee()->getSourceRange(), CallType);
@@ -2589,7 +2599,7 @@ bool Sema::CheckPointerCall(NamedDecl *NDecl, CallExpr *TheCall,
bool Sema::CheckOtherCall(CallExpr *TheCall, const FunctionProtoType *Proto) {
VariadicCallType CallType = getVariadicCallType(/*FDecl=*/nullptr, Proto,
TheCall->getCallee());
- checkCall(/*FDecl=*/nullptr, Proto,
+ checkCall(/*FDecl=*/nullptr, Proto, /*ThisArg=*/nullptr,
llvm::makeArrayRef(TheCall->getArgs(), TheCall->getNumArgs()),
/*IsMemberFunction=*/false, TheCall->getRParenLoc(),
TheCall->getCallee()->getSourceRange(), CallType);
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index e1e1f02836..0077d6c539 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -342,7 +342,6 @@ 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);
@@ -365,11 +364,8 @@ 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);
+ if (diagnoseArgIndependentDiagnoseIfAttrs(FD, Loc))
return true;
- }
}
// [OpenMP 4.0], 2.15 declare reduction Directive, Restrictions
@@ -385,9 +381,6 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc,
return true;
}
- for (const auto *W : DiagnoseIfWarnings)
- emitDiagnoseIfDiagnostic(Loc, W);
-
DiagnoseAvailabilityOfDecl(*this, D, Loc, UnknownObjCClass,
ObjCPropertyAccess);
@@ -5189,16 +5182,6 @@ static void checkDirectCallValidity(Sema &S, const Expr *Fn,
<< 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.
diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp
index b2fb33f534..3de677e37b 100644
--- a/lib/Sema/SemaExprCXX.cpp
+++ b/lib/Sema/SemaExprCXX.cpp
@@ -6712,6 +6712,11 @@ ExprResult Sema::BuildCXXMemberCallExpr(Expr *E, NamedDecl *FoundDecl,
CXXMemberCallExpr *CE =
new (Context) CXXMemberCallExpr(Context, ME, None, ResultType, VK,
Exp.get()->getLocEnd());
+
+ if (CheckFunctionCall(Method, CE,
+ Method->getType()->castAs<FunctionProtoType>()))
+ return ExprError();
+
return CE;
}
diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp
index 883e2ae264..38a7b8c127 100644
--- a/lib/Sema/SemaLookup.cpp
+++ b/lib/Sema/SemaLookup.cpp
@@ -2960,7 +2960,6 @@ 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,
@@ -2973,7 +2972,7 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *RD,
if (SM == CXXCopyAssignment || SM == CXXMoveAssignment)
AddMethodTemplateCandidate(
Tmpl, Cand, RD, nullptr, ThisTy, Classification,
- /*ThisArg=*/nullptr, llvm::makeArrayRef(&Arg, NumArgs), OCS, true);
+ llvm::makeArrayRef(&Arg, NumArgs), OCS, true);
else if (CtorInfo)
AddTemplateOverloadCandidate(
CtorInfo.ConstructorTmpl, CtorInfo.FoundDecl, nullptr,
diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp
index c66fe7678d..f976b76727 100644
--- a/lib/Sema/SemaOverload.cpp
+++ b/lib/Sema/SemaOverload.cpp
@@ -839,20 +839,12 @@ void OverloadCandidateSet::destroyCandidates() {
void OverloadCandidateSet::clear() {
destroyCandidates();
- // 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 {
@@ -5831,28 +5823,6 @@ 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
@@ -5886,10 +5856,9 @@ Sema::AddOverloadCandidate(FunctionDecl *Function,
// object argument (C++ [over.call.func]p3), and the acting context
// is irrelevant.
AddMethodCandidate(Method, FoundDecl, Method->getParent(), QualType(),
- Expr::Classification::makeSimpleLValue(),
- /*ThisArg=*/nullptr, Args, CandidateSet,
- SuppressUserConversions, PartialOverloading,
- EarlyConversions);
+ Expr::Classification::makeSimpleLValue(), Args,
+ CandidateSet, SuppressUserConversions,
+ PartialOverloading, EarlyConversions);
return;
}
// We treat a constructor like a non-member function, since its object
@@ -6050,8 +6019,6 @@ Sema::AddOverloadCandidate(FunctionDecl *Function,
Candidate.FailureKind = ovl_fail_ext_disabled;
return;
}
-
- initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Function, Args);
}
ObjCMethodDecl *
@@ -6260,85 +6227,73 @@ 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);
- }
+template <typename CheckFn>
+static bool diagnoseDiagnoseIfAttrsWith(Sema &S, const FunctionDecl *FD,
+ bool ArgDependent, SourceLocation Loc,
+ CheckFn &&IsSuccessful) {
+ SmallVector<const DiagnoseIfAttr *, 8> Attrs;
+ for (const auto *DIA : FD->specific_attrs<DiagnoseIfAttr>()) {
+ if (ArgDependent == DIA->getArgDependent())
+ Attrs.push_back(DIA);
+ }
- return !Errors.empty() || !Nonfatal.empty();
-}
+ // Common case: No diagnose_if attributes, so we can quit early.
+ if (Attrs.empty())
+ return false;
+
+ auto WarningBegin = std::stable_partition(
+ Attrs.begin(), Attrs.end(),
+ [](const DiagnoseIfAttr *DIA) { return DIA->isError(); });
-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;
+ auto ErrAttr = llvm::find_if(llvm::make_range(Attrs.begin(), WarningBegin),
+ IsSuccessful);
+ if (ErrAttr != WarningBegin) {
+ const DiagnoseIfAttr *DIA = *ErrAttr;
+ S.Diag(Loc, diag::err_diagnose_if_succeeded) << DIA->getMessage();
+ S.Diag(DIA->getLocation(), diag::note_from_diagnose_if)
+ << DIA->getParent() << DIA->getCond()->getSourceRange();
+ return true;
+ }
- SFINAETrap Trap(*this);
- SmallVector<Expr *, 16> ConvertedArgs;
- Expr *ConvertedThis;
- if (!convertArgsForAvailabilityChecks(*this, Function, ThisArg, Args, Trap,
- MissingImplicitThis, ConvertedThis,
- ConvertedArgs))
- return nullptr;
+ for (const auto *DIA : llvm::make_range(WarningBegin, Attrs.end()))
+ if (IsSuccessful(DIA)) {
+ S.Diag(Loc, diag::warn_diagnose_if_succeeded) << DIA->getMessage();
+ S.Diag(DIA->getLocation(), diag::note_from_diagnose_if)
+ << DIA->getParent() << DIA->getCond()->getSourceRange();
+ }
- 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();
- });
+ return false;
}
-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;
- });
+bool Sema::diagnoseArgDependentDiagnoseIfAttrs(const FunctionDecl *Function,
+ const Expr *ThisArg,
+ ArrayRef<const Expr *> Args,
+ SourceLocation Loc) {
+ return diagnoseDiagnoseIfAttrsWith(
+ *this, Function, /*ArgDependent=*/true, Loc,
+ [&](const DiagnoseIfAttr *DIA) {
+ APValue Result;
+ // It's sane to use the same Args 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(), Args, ThisArg))
+ return false;
+ return Result.isInt() && Result.getInt().getBoolValue();
+ });
}
-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();
+bool Sema::diagnoseArgIndependentDiagnoseIfAttrs(const FunctionDecl *Function,
+ SourceLocation Loc) {
+ return diagnoseDiagnoseIfAttrsWith(
+ *this, Function, /*ArgDependent=*/false, Loc,
+ [&](const DiagnoseIfAttr *DIA) {
+ bool Result;
+ return DIA->getCond()->EvaluateAsBooleanCondition(Result, Context) &&
+ Result;
+ });
}
/// \brief Add all of the function declarations in the given function set to
@@ -6356,8 +6311,8 @@ void Sema::AddFunctionCandidates(const UnresolvedSetImpl &Fns,
AddMethodCandidate(cast<CXXMethodDecl>(FD), F.getPair(),
cast<CXXMethodDecl>(FD)->getParent(),
Args[0]->getType(), Args[0]->Classify(Context),
- Args[0], Args.slice(1), CandidateSet,
- SuppressUserConversions, PartialOverloading);
+ Args.slice(1), CandidateSet, SuppressUserConversions,
+ PartialOverloading);
else
AddOverloadCandidate(FD, F.getPair(), Args, CandidateSet,
SuppressUserConversions, PartialOverloading);
@@ -6369,7 +6324,7 @@ void Sema::AddFunctionCandidates(const UnresolvedSetImpl &Fns,
FunTmpl, F.getPair(),
cast<CXXRecordDecl>(FunTmpl->getDeclContext()),
ExplicitTemplateArgs, Args[0]->getType(),
- Args[0]->Classify(Context), Args[0], Args.slice(1), CandidateSet,
+ Args[0]->Classify(Context), Args.slice(1), CandidateSet,
SuppressUserConversions, PartialOverloading);
else
AddTemplateOverloadCandidate(FunTmpl, F.getPair(),
@@ -6385,7 +6340,6 @@ 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) {
@@ -6399,15 +6353,13 @@ void Sema::AddMethodCandidate(DeclAccessPair FoundDecl,
assert(isa<CXXMethodDecl>(TD->getTemplatedDecl()) &&
"Expected a member function template");
AddMethodTemplateCandidate(TD, FoundDecl, ActingContext,
- /*ExplicitArgs*/ nullptr,
- ObjectType, ObjectClassification,
- ThisArg, Args, CandidateSet,
+ /*ExplicitArgs*/ nullptr, ObjectType,
+ ObjectClassification, Args, CandidateSet,
SuppressUserConversions);
} else {
AddMethodCandidate(cast<CXXMethodDecl>(Decl), FoundDecl, ActingContext,
- ObjectType, ObjectClassification,
- ThisArg, Args,
- CandidateSet, SuppressUserConversions);
+ ObjectType, ObjectClassification, Args, CandidateSet,
+ SuppressUserConversions);
}
}
@@ -6422,7 +6374,7 @@ void
Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
CXXRecordDecl *ActingContext, QualType ObjectType,
Expr::Classification ObjectClassification,
- Expr *ThisArg, ArrayRef<Expr *> Args,
+ ArrayRef<Expr *> Args,
OverloadCandidateSet &CandidateSet,
bool SuppressUserConversions,
bool PartialOverloading,
@@ -6544,9 +6496,6 @@ 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
@@ -6559,7 +6508,6 @@ Sema::AddMethodTemplateCandidate(FunctionTemplateDecl *MethodTmpl,
TemplateArgumentListInfo *ExplicitTemplateArgs,
QualType ObjectType,
Expr::Classification ObjectClassification,
- Expr *ThisArg,
ArrayRef<Expr *> Args,
OverloadCandidateSet& CandidateSet,
bool SuppressUserConversions,
@@ -6613,9 +6561,9 @@ Sema::AddMethodTemplateCandidate(FunctionTemplateDecl *MethodTmpl,
assert(isa<CXXMethodDecl>(Specialization) &&
"Specialization is not a member function?");
AddMethodCandidate(cast<CXXMethodDecl>(Specialization), FoundDecl,
- ActingContext, ObjectType, ObjectClassification,
- /*ThisArg=*/ThisArg, Args, CandidateSet,
- SuppressUserConversions, PartialOverloading, Conversions);
+ ActingContext, ObjectType, ObjectClassification, Args,
+ CandidateSet, SuppressUserConversions, PartialOverloading,
+ Conversions);
}
/// \brief Add a C++ function template specialization as a candidate
@@ -6942,8 +6890,6 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion,
Candidate.DeductionFailure.Data = FailedAttr;
return;
}
-
- initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Conversion, None, false, From);
}
/// \brief Adds a conversion function template specialization
@@ -7096,8 +7042,6 @@ 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
@@ -7146,7 +7090,7 @@ void Sema::AddMemberOperatorCandidates(OverloadedOperatorKind Op,
Oper != OperEnd;
++Oper)
AddMethodCandidate(Oper.getPair(), Args[0]->getType(),
- Args[0]->Classify(Context), Args[0], Args.slice(1),
+ Args[0]->Classify(Context), Args.slice(1),
CandidateSet, /*SuppressUserConversions=*/false);
}
}
@@ -9178,17 +9122,6 @@ 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.
///
@@ -9267,19 +9200,13 @@ OverloadCandidateSet::BestViableFunction(Sema &S, SourceLocation Loc,
// Best is the best viable function.
if (Best->Function &&
(Best->Function->isDeleted() ||
- S.isFunctionConsideredUnavailable(Best->Function) ||
- isCandidateUnavailableDueToDiagnoseIf(*Best)))
+ S.isFunctionConsideredUnavailable(Best->Function)))
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;
}
@@ -10162,14 +10089,6 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand,
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.
S.NoteOverloadCandidate(Cand->FoundDecl, Fn);
@@ -12113,6 +12032,10 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, UnaryOperatorKind Opc,
if (CheckCallReturnType(FnDecl->getReturnType(), OpLoc, TheCall, FnDecl))
return ExprError();
+ if (CheckFunctionCall(FnDecl, TheCall,
+ FnDecl->getType()->castAs<FunctionProtoType>()))
+ return ExprError();
+
return MaybeBindToTemporary(TheCall);
} else {
// We matched a built-in operator. Convert the arguments, then
@@ -12343,16 +12266,20 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
return ExprError();
ArrayRef<const Expr *> ArgsArray(Args, 2);
+ const Expr *ImplicitThis = nullptr;
// Cut off the implicit 'this'.
- if (isa<CXXMethodDecl>(FnDecl))
+ if (isa<CXXMethodDecl>(FnDecl)) {
+ ImplicitThis = ArgsArray[0];
ArgsArray = ArgsArray.slice(1);
+ }
// Check for a self move.
if (Op == OO_Equal)
DiagnoseSelfMove(Args[0], Args[1], OpLoc);
- checkCall(FnDecl, nullptr, ArgsArray, isa<CXXMethodDecl>(FnDecl), OpLoc,
- TheCall->getSourceRange(), VariadicDoesNotApply);
+ checkCall(FnDecl, nullptr, ImplicitThis, ArgsArray,
+ isa<CXXMethodDecl>(FnDecl), OpLoc, TheCall->getSourceRange(),
+ VariadicDoesNotApply);
return MaybeBindToTemporary(TheCall);
} else {
@@ -12561,6 +12488,10 @@ Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
if (CheckCallReturnType(FnDecl->getReturnType(), LLoc, TheCall, FnDecl))
return ExprError();
+ if (CheckFunctionCall(Method, TheCall,
+ Method->getType()->castAs<FunctionProtoType>()))
+ return ExprError();
+
return MaybeBindToTemporary(TheCall);
} else {
// We matched a built-in operator. Convert the arguments, then
@@ -12727,16 +12658,6 @@ 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) {
@@ -12757,14 +12678,12 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
continue;
AddMethodCandidate(Method, I.getPair(), ActingDC, ObjectType,
- ObjectClassification,
- /*ThisArg=*/GetUnresolvedBase(), Args, CandidateSet,
+ ObjectClassification, Args, CandidateSet,
/*SuppressUserConversions=*/false);
} else {
AddMethodTemplateCandidate(
cast<FunctionTemplateDecl>(Func), I.getPair(), ActingDC,
- TemplateArgs, ObjectType, ObjectClassification,
- /*ThisArg=*/GetUnresolvedBase(), Args, CandidateSet,
+ TemplateArgs, ObjectType, ObjectClassification, Args, CandidateSet,
/*SuppressUsedConversions=*/false);
}
}
@@ -12882,16 +12801,6 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
<< 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) ||
@@ -12970,9 +12879,8 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
for (LookupResult::iterator Oper = R.begin(), OperEnd = R.end();
Oper != OperEnd; ++Oper) {
AddMethodCandidate(Oper.getPair(), Object.get()->getType(),
- Object.get()->Classify(Context),
- Object.get(), Args, CandidateSet,
- /*SuppressUserConversions=*/ false);
+ Object.get()->Classify(Context), Args, CandidateSet,
+ /*SuppressUserConversions=*/false);
}
// C++ [over.call.object]p2:
@@ -13247,8 +13155,7 @@ 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),
- Base, None, CandidateSet,
- /*SuppressUserConversions=*/false);
+ None, CandidateSet, /*SuppressUserConversions=*/false);
}
bool HadMultipleCandidates = (CandidateSet.size() > 1);
@@ -13322,7 +13229,11 @@ Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc,
Base, ResultTy, VK, OpLoc, false);
if (CheckCallReturnType(Method->getReturnType(), OpLoc, TheCall, Method))
- return ExprError();
+ return ExprError();
+
+ if (CheckFunctionCall(Method, TheCall,
+ Method->getType()->castAs<FunctionProtoType>()))
+ return ExprError();
return MaybeBindToTemporary(TheCall);
}
diff --git a/test/Sema/diagnose_if.c b/test/Sema/diagnose_if.c
index 6dd8bafe8b..27689f49b4 100644
--- a/test/Sema/diagnose_if.c
+++ b/test/Sema/diagnose_if.c
@@ -70,14 +70,14 @@ void runVariable() {
#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 ovl1(const char *n) _overloadable _diagnose_if(n, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
+int ovl1(void *m) _overloadable;
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}}
+ ovl1(""); // expected-error{{oh no}}
ovl2((void *)0); // expected-error{{ambiguous}}
}
diff --git a/test/SemaCXX/diagnose_if.cpp b/test/SemaCXX/diagnose_if.cpp
index f97b79d035..02b3620e76 100644
--- a/test/SemaCXX/diagnose_if.cpp
+++ b/test/SemaCXX/diagnose_if.cpp
@@ -2,6 +2,8 @@
#define _diagnose_if(...) __attribute__((diagnose_if(__VA_ARGS__)))
+using size_t = decltype(sizeof(int));
+
namespace type_dependent {
template <typename T>
void neverok() _diagnose_if(!T(), "oh no", "error") {} // expected-note 4{{from 'diagnose_if'}}
@@ -51,14 +53,14 @@ void runAll() {
}
template <typename T>
-void errorIf(T a) _diagnose_if(T() != a, "oh no", "error") {} // expected-note {{candidate disabled: oh no}}
+void errorIf(T a) _diagnose_if(T() != a, "oh no", "error") {} // expected-note{{from 'diagnose_if'}}
template <typename T>
-void warnIf(T a) _diagnose_if(T() != a, "oh no", "warning") {} // expected-note {{from 'diagnose_if'}}
+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}}
+ errorIf(1); // expected-error{{oh no}}
warnIf(0);
warnIf(1); // expected-warning{{oh no}}
@@ -114,14 +116,14 @@ void runAll() {
}
template <int N>
-void errorIf(int a) _diagnose_if(N != a, "oh no", "error") {} // expected-note {{candidate disabled: oh no}}
+void errorIf(int a) _diagnose_if(N != a, "oh no", "error") {} // expected-note{{from 'diagnose_if'}}
template <int N>
-void warnIf(int a) _diagnose_if(N != a, "oh no", "warning") {} // expected-note {{from 'diagnose_if'}}
+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}}
+ errorIf<0>(1); // expected-error{{oh no}}
warnIf<0>(0);
warnIf<0>(1); // expected-warning{{oh no}}
@@ -135,8 +137,8 @@ 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 fooArg(int a) _diagnose_if(a, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
+void fooArg(short);
void barArg(int);
void barArg(short a) _diagnose_if(a, "oh no", "error");
@@ -145,7 +147,7 @@ void runAll() {
foo(1); // expected-error{{oh no}}
bar(1);
- fooArg(1); // expected-error{{call to unavailable function}}
+ fooArg(1); // expected-error{{oh no}}
barArg(1);
auto p = foo; // expected-error{{incompatible initializer of type '<overloaded function type>'}}
@@ -188,11 +190,11 @@ 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 fooOvl(int i) _diagnose_if(i, "int bad i", "error"); // expected-note{{from 'diagnose_if'}}
+ void fooOvl(short i) _diagnose_if(i, "short bad i", "error"); // expected-note{{from 'diagnose_if'}}
- 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 barOvl(int i) _diagnose_if(i != T(), "int bad i", "error"); // expected-note{{from 'diagnose_if'}}
+ void barOvl(short i) _diagnose_if(i != T(), "short bad i", "error"); // expected-note{{from 'diagnose_if'}}
};
void runErrors() {
@@ -203,14 +205,14 @@ void runErrors() {
Errors<int>().bar(1); // expected-error{{bad i}}
Errors<int>().fooOvl(0);
- Errors<int>().fooOvl(1); // expected-error{{call to unavailable}}
+ Errors<int>().fooOvl(1); // expected-error{{int bad i}}
Errors<int>().fooOvl(short(0));
- Errors<int>().fooOvl(short(1)); // expected-error{{call to unavailable}}
+ Errors<int>().fooOvl(short(1)); // expected-error{{short bad i}}
Errors<int>().barOvl(0);
- Errors<int>().barOvl(1); // expected-error{{call to unavailable}}
+ Errors<int>().barOvl(1); // expected-error{{int bad i}}
Errors<int>().barOvl(short(0));
- Errors<int>().barOvl(short(1)); // expected-error{{call to unavailable}}
+ Errors<int>().barOvl(short(1)); // expected-error{{short bad i}}
}
template <typename T>
@@ -275,8 +277,8 @@ 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 bar() _diagnose_if(foo(), "bad foo", "error"); // expected-note{{from 'diagnose_if'}}
+void bar(int a) _diagnose_if(foo(a), "bad foo", "error"); // expected-note{{from 'diagnose_if'}}
void early() {
bar();
@@ -290,7 +292,7 @@ constexpr int foo(int a) { return a; }
void late() {
bar(); // expected-error{{bad foo}}
bar(0);
- bar(1); // expected-error{{call to unavailable function}}
+ bar(1); // expected-error{{bad foo}}
}
}
@@ -301,11 +303,11 @@ struct Foo {
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}}
+ operator int() const _diagnose_if(isFooable(), "oh no", "error") { return 1; } // expected-note{{from 'diagnose_if'}}
- void go2() const _diagnose_if(isFooable(), "oh no", "error") // expected-note{{oh no}}
+ void go2() const _diagnose_if(isFooable(), "oh no", "error") // expected-note{{from 'diagnose_if'}}
__attribute__((enable_if(true, ""))) {}
- void go2() const _diagnose_if(isFooable(), "oh no", "error") {} // expected-note{{oh no}}
+ void go2() const _diagnose_if(isFooable(), "oh no", "error") {}
constexpr int go3() const _diagnose_if(isFooable(), "oh no", "error")
__attribute__((enable_if(true, ""))) {
@@ -326,20 +328,20 @@ struct Foo {
}
};
-void go(const Foo &f) _diagnose_if(f.isFooable(), "oh no", "error") {} // expected-note{{oh no}}
+void go(const Foo &f) _diagnose_if(f.isFooable(), "oh no", "error") {} // expected-note{{from 'diagnose_if'}}
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}}
+ (void)int(Foo(1)); // expected-error{{oh no}}
Foo(0).go2();
- Foo(1).go2(); // expected-error{{call to unavailable member function}}
+ Foo(1).go2(); // expected-error{{oh no}}
go(Foo(0));
- go(Foo(1)); // expected-error{{call to unavailable function}}
+ go(Foo(1)); // expected-error{{oh no}}
}
}
@@ -349,17 +351,17 @@ struct Foo {
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}}
+ template <typename T> T getVal() _diagnose_if(bad(), "oh no", "error") { // expected-note{{from 'diagnose_if'}}
return T();
}
template <typename T>
- constexpr T getVal2() const _diagnose_if(bad(), "oh no", "error") { // expected-note{{oh no}}
+ constexpr T getVal2() const _diagnose_if(bad(), "oh no", "error") { // expected-note{{from 'diagnose_if'}}
return T();
}
template <typename T>
- constexpr operator T() const _diagnose_if(bad(), "oh no", "error") { // expected-note{{oh no}}
+ constexpr operator T() const _diagnose_if(bad(), "oh no", "error") { // expected-note{{from 'diagnose_if'}}
return T();
}
@@ -369,13 +371,13 @@ struct Foo {
void run() {
Foo(0).getVal<int>();
- Foo(1).getVal<int>(); // expected-error{{call to unavailable member function}}
+ Foo(1).getVal<int>(); // expected-error{{oh no}}
Foo(0).getVal2<int>();
- Foo(1).getVal2<int>(); // expected-error{{call to unavailable member function}}
+ Foo(1).getVal2<int>(); // expected-error{{oh no}}
(void)int(Foo(0));
- (void)int(Foo(1)); // expected-error{{uses deleted function}}
+ (void)int(Foo(1)); // expected-error{{oh no}}
}
}
@@ -385,18 +387,18 @@ 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}}
+ const Bar *operator->() const _diagnose_if(bad(), "oh no", "error") { // expected-note{{from 'diagnose_if'}}
return nullptr;
}
- void operator()() const _diagnose_if(bad(), "oh no", "error") {} // expected-note{{oh no}}
+ void operator()() const _diagnose_if(bad(), "oh no", "error") {} // expected-note{{from 'diagnose_if'}}
};
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}}
+ void operator()(double) const _diagnose_if(bad(), "oh no", "error") {} // expected-note{{from 'diagnose_if'}}
+ void operator()(int) const _diagnose_if(bad(), "oh no", "error") {} // expected-note{{from 'diagnose_if'}}
};
struct ParenTemplate {
@@ -404,33 +406,70 @@ struct ParenTemplate {
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 operator()(T) const _diagnose_if(bad(), "oh no", "error") {} // expected-note 2{{from 'diagnose_if'}}
};
void run() {
(void)Foo(0)->j;
- (void)Foo(1)->j; // expected-error{{selected unavailable operator '->'}}
+ (void)Foo(1)->j; // expected-error{{oh no}}
Foo(0)();
- Foo(1)(); // expected-error{{unavailable function call operator}}
+ Foo(1)(); // expected-error{{oh no}}
ParenOverload(0)(1);
ParenOverload(0)(1.);
- ParenOverload(1)(1); // expected-error{{unavailable function call operator}}
- ParenOverload(1)(1.); // expected-error{{unavailable function call operator}}
+ ParenOverload(1)(1); // expected-error{{oh no}}
+ ParenOverload(1)(1.); // expected-error{{oh no}}
ParenTemplate(0)(1);
ParenTemplate(0)(1.);
- ParenTemplate(1)(1); // expected-error{{unavailable function call operator}}
- ParenTemplate(1)(1.); // expected-error{{unavailable function call operator}}
+ ParenTemplate(1)(1); // expected-error{{oh no}}
+ ParenTemplate(1)(1.); // expected-error{{oh no}}
}
void runLambda() {
- auto L1 = [](int i) _diagnose_if(i, "oh no", "error") {}; // expected-note{{oh no}} expected-note{{conversion candidate}}
+ auto L1 = [](int i) _diagnose_if(i, "oh no", "error") {}; // expected-note{{from 'diagnose_if'}}
L1(0);
- L1(1); // expected-error{{call to unavailable function call}}
+ L1(1); // expected-error{{oh no}}
+}
+
+struct Brackets {
+ int i;
+ constexpr Brackets(int i): i(i) {}
+ void operator[](int) _diagnose_if(i == 1, "oh no", "warning") // expected-note{{from 'diagnose_if'}}
+ _diagnose_if(i == 2, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
+};
+
+void runBrackets(int i) {
+ Brackets{0}[i];
+ Brackets{1}[i]; // expected-warning{{oh no}}
+ Brackets{2}[i]; // expected-error{{oh no}}
+}
+
+struct Unary {
+ int i;
+ constexpr Unary(int i): i(i) {}
+ void operator+() _diagnose_if(i == 1, "oh no", "warning") // expected-note{{from 'diagnose_if'}}
+ _diagnose_if(i == 2, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
+};
+
+void runUnary() {
+ +Unary{0};
+ +Unary{1}; // expected-warning{{oh no}}
+ +Unary{2}; // expected-error{{oh no}}
+}
+
+struct PostInc {
+ void operator++(int i) _diagnose_if(i == 1, "oh no", "warning") // expected-note{{from 'diagnose_if'}}
+ _diagnose_if(i == 2, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
+};
+
+void runPostInc() {
+ PostInc{}++;
+ PostInc{}.operator++(1); // expected-warning{{oh no}}
+ PostInc{}.operator++(2); // expected-error{{oh no}}
}
}
@@ -439,22 +478,192 @@ 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") {
+ constexpr const Foo &operator=(const Foo &) const
+ _diagnose_if(I, "oh no", "error") { // expected-note{{from 'diagnose_if'}}
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") {
+ constexpr const Foo &operator=(const Foo &&) const
+ _diagnose_if(I, "oh no", "error") { // expected-note{{from 'diagnose_if'}}
return *this;
}
};
+struct Bar {
+ int I;
+ constexpr Bar(int I) _diagnose_if(I == 1, "oh no", "warning") // expected-note{{from 'diagnose_if'}}
+ _diagnose_if(I == 2, "oh no", "error"): I(I) {} // expected-note{{from 'diagnose_if'}}
+};
+
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}}
+ F2 = F; // expected-error{{oh no}}
+ F2 = Foo{2}; // expected-error{{oh no}}
+
+ Bar{0};
+ Bar{1}; // expected-warning{{oh no}}
+ Bar{2}; // expected-error{{oh no}}
+}
+}
+
+namespace ref_init {
+struct Bar {};
+struct Baz {};
+struct Foo {
+ int i;
+ constexpr Foo(int i): i(i) {}
+ operator const Bar &() const _diagnose_if(i, "oh no", "warning"); // expected-note{{from 'diagnose_if'}}
+ operator const Baz &() const _diagnose_if(i, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
+};
+void fooBar(const Bar &b);
+void fooBaz(const Baz &b);
+
+void run() {
+ fooBar(Foo{0});
+ fooBar(Foo{1}); // expected-warning{{oh no}}
+ fooBaz(Foo{0});
+ fooBaz(Foo{1}); // expected-error{{oh no}}
+}
+}
+
+namespace udl {
+void operator""_fn(char c)_diagnose_if(c == 1, "oh no", "warning") // expected-note{{from 'diagnose_if'}}
+ _diagnose_if(c == 2, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
+
+void run() {
+ '\0'_fn;
+ '\1'_fn; // expected-warning{{oh no}}
+ '\2'_fn; // expected-error{{oh no}}
+}
+}
+
+namespace PR31638 {
+struct String {
+ String(char const* __s) _diagnose_if(__s == nullptr, "oh no ptr", "warning"); // expected-note{{from 'diagnose_if'}}
+ String(int __s) _diagnose_if(__s != 0, "oh no int", "warning"); // expected-note{{from 'diagnose_if'}}
+};
+
+void run() {
+ String s(nullptr); // expected-warning{{oh no ptr}}
+ String ss(42); // expected-warning{{oh no int}}
+}
+}
+
+namespace PR31639 {
+struct Foo {
+ Foo(int I) __attribute__((diagnose_if(I, "oh no", "error"))); // expected-note{{from 'diagnose_if'}}
+};
+
+void bar() { Foo f(1); } // expected-error{{oh no}}
+}
+
+namespace user_defined_conversion {
+struct Foo {
+ int i;
+ constexpr Foo(int i): i(i) {}
+ operator size_t() const _diagnose_if(i == 1, "oh no", "warning") // expected-note{{from 'diagnose_if'}}
+ _diagnose_if(i == 2, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
+};
+
+void run() {
+ // `new T[N]`, where N is implicitly convertible to size_t, calls
+ // PerformImplicitConversion directly. This lets us test the diagnostic logic
+ // in PerformImplicitConversion.
+ new int[Foo{0}];
+ new int[Foo{1}]; // expected-warning{{oh no}}
+ new int[Foo{2}]; // expected-error{{oh no}}
+}
+}
+
+namespace std {
+ template <typename T>
+ struct initializer_list {
+ const T *ptr;
+ size_t elems;
+
+ constexpr size_t size() const { return elems; }
+ };
+}
+
+namespace initializer_lists {
+struct Foo {
+ Foo(std::initializer_list<int> l)
+ _diagnose_if(l.size() == 1, "oh no", "warning") // expected-note{{from 'diagnose_if'}}
+ _diagnose_if(l.size() == 2, "oh no", "error") {} // expected-note{{from 'diagnose_if'}}
+};
+
+void run() {
+ Foo{std::initializer_list<int>{}};
+ Foo{std::initializer_list<int>{1}}; // expected-warning{{oh no}}
+ Foo{std::initializer_list<int>{1, 2}}; // expected-error{{oh no}}
+ Foo{std::initializer_list<int>{1, 2, 3}};
+}
+}
+
+namespace range_for_loop {
+ namespace adl {
+ struct Foo {
+ int i;
+ constexpr Foo(int i): i(i) {}
+ };
+ void **begin(const Foo &f) _diagnose_if(f.i, "oh no", "warning");
+ void **end(const Foo &f) _diagnose_if(f.i, "oh no", "warning");
+
+ struct Bar {
+ int i;
+ constexpr Bar(int i): i(i) {}
+ };
+ void **begin(const Bar &b) _diagnose_if(b.i, "oh no", "error");
+ void **end(const Bar &b) _diagnose_if(b.i, "oh no", "error");
+ }
+
+ void run() {
+ for (void *p : adl::Foo(0)) {}
+ // FIXME: This should emit diagnostics. It seems that our constexpr
+ // evaluator isn't able to evaluate `adl::Foo(1)` as a constant, though.
+ for (void *p : adl::Foo(1)) {}
+
+ for (void *p : adl::Bar(0)) {}
+ // FIXME: Same thing.
+ for (void *p : adl::Bar(1)) {}
+ }
+}
+
+namespace operator_new {
+struct Foo {
+ int j;
+ static void *operator new(size_t i) _diagnose_if(i, "oh no", "warning");
+};
+
+struct Bar {
+ int j;
+ static void *operator new(size_t i) _diagnose_if(!i, "oh no", "warning");
+};
+
+void run() {
+ // FIXME: This should emit a diagnostic.
+ new Foo();
+ // This is here because we sometimes pass a dummy argument `operator new`. We
+ // should ignore this, rather than complaining about it.
+ new Bar();
+}
+}
+
+namespace contextual_implicit_conv {
+struct Foo {
+ int i;
+ constexpr Foo(int i): i(i) {}
+ constexpr operator int() const _diagnose_if(i == 1, "oh no", "warning") // expected-note{{from 'diagnose_if'}}
+ _diagnose_if(i == 2, "oh no", "error") { // expected-note{{from 'diagnose_if'}}
+ return i;
+ }
+};
+
+void run() {
+ switch (constexpr Foo i = 0) { default: break; }
+ switch (constexpr Foo i = 1) { default: break; } // expected-warning{{oh no}}
+ switch (constexpr Foo i = 2) { default: break; } // expected-error{{oh no}}
}
}