Change how we handle diagnose_if attributes.
This patch changes how we handle argument-dependent `diagnose_if`
attributes. In particular, we now check them in the same place that we
check for things like passing NULL to Nonnull args, etc. This is
basically better in every way than how we were handling them before. :)
This fixes PR31638, PR31639, and PR31640.
Differential Revision: https://reviews.llvm.org/D28889
llvm-svn: 293360
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index 88fdc99..5220d98 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -675,26 +675,6 @@
/// 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 @@
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 @@
/// 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 @@
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/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 63ca474..a405d2b 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2545,14 +2545,14 @@
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,
@@ -2563,7 +2563,6 @@
TemplateArgumentListInfo *ExplicitTemplateArgs,
QualType ObjectType,
Expr::Classification ObjectClassification,
- Expr *ThisArg,
ArrayRef<Expr *> Args,
OverloadCandidateSet& CandidateSet,
bool SuppressUserConversions = false,
@@ -2637,37 +2636,27 @@
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.
@@ -9937,8 +9926,8 @@
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/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 49208e2..3aedb2a 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -2426,11 +2426,12 @@
}
/// 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 @@
CheckArgumentWithTypeTag(I, Args.data());
}
}
+
+ if (FD)
+ diagnoseArgDependentDiagnoseIfAttrs(FD, ThisArg, Args, Loc);
}
/// CheckConstructorCall - Check a constructor call for correctness and safety
@@ -2487,8 +2491,8 @@
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 @@
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 @@
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 @@
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::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/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index ca57a41..95b4b72 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -342,7 +342,6 @@
}
// 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 @@
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 @@
return true;
}
- for (const auto *W : DiagnoseIfWarnings)
- emitDiagnoseIfDiagnostic(Loc, W);
-
DiagnoseAvailabilityOfDecl(*this, D, Loc, UnknownObjCClass,
ObjCPropertyAccess);
@@ -5190,16 +5183,6 @@
<< 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/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index f4df3ca..b5a0344 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -6772,6 +6772,11 @@
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/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index 84dc779..7066215 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -2961,7 +2961,6 @@
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,
@@ -2974,7 +2973,7 @@
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/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 83b679a..05caf92 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -839,20 +839,12 @@
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 @@
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 @@
// 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 @@
Candidate.FailureKind = ovl_fail_ext_disabled;
return;
}
-
- initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Function, Args);
}
ObjCMethodDecl *
@@ -6260,85 +6227,73 @@
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) {
+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);
+ }
+
+ // 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(); });
+
// 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;
+ 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;
+ }
- llvm::erase_if(Nonfatal, [&](DiagnoseIfAttr *A) { return !IsSuccessful(A); });
- 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 false;
}
-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();
- });
+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();
+ });
}
-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();
+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 @@
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 @@
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::AddMethodCandidate(DeclAccessPair FoundDecl,
QualType ObjectType,
Expr::Classification ObjectClassification,
- Expr *ThisArg,
ArrayRef<Expr *> Args,
OverloadCandidateSet& CandidateSet,
bool SuppressUserConversions) {
@@ -6399,15 +6353,13 @@
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 @@
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 @@
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 @@
TemplateArgumentListInfo *ExplicitTemplateArgs,
QualType ObjectType,
Expr::Classification ObjectClassification,
- Expr *ThisArg,
ArrayRef<Expr *> Args,
OverloadCandidateSet& CandidateSet,
bool SuppressUserConversions,
@@ -6613,9 +6561,9 @@
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 @@
Candidate.DeductionFailure.Data = FailedAttr;
return;
}
-
- initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Conversion, None, false, From);
}
/// \brief Adds a conversion function template specialization
@@ -7096,8 +7042,6 @@
Candidate.DeductionFailure.Data = FailedAttr;
return;
}
-
- initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Conversion, None);
}
/// \brief Add overload candidates for overloaded operators that are
@@ -7146,7 +7090,7 @@
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 @@
}
}
-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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
<< 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 @@
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 @@
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 @@
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/clang/test/Sema/diagnose_if.c b/clang/test/Sema/diagnose_if.c
index 6dd8baf..27689f4 100644
--- a/clang/test/Sema/diagnose_if.c
+++ b/clang/test/Sema/diagnose_if.c
@@ -70,14 +70,14 @@
#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/clang/test/SemaCXX/diagnose_if.cpp b/clang/test/SemaCXX/diagnose_if.cpp
index f97b79d..5a9567f 100644
--- a/clang/test/SemaCXX/diagnose_if.cpp
+++ b/clang/test/SemaCXX/diagnose_if.cpp
@@ -2,6 +2,8 @@
#define _diagnose_if(...) __attribute__((diagnose_if(__VA_ARGS__)))
+using size_t = unsigned long;
+
namespace type_dependent {
template <typename T>
void neverok() _diagnose_if(!T(), "oh no", "error") {} // expected-note 4{{from 'diagnose_if'}}
@@ -51,14 +53,14 @@
}
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 @@
}
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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
};
-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 @@
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 @@
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 @@
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 @@
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 @@
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}}
}
}