/// to be used while performing partial ordering of function templates.
unsigned ExplicitCallArguments;
- /// The number of diagnose_if attributes that this overload triggered.
- /// If any of the triggered attributes are errors, this won't count
- /// diagnose_if warnings.
- unsigned NumTriggeredDiagnoseIfs = 0;
-
- /// Basically a TinyPtrVector<DiagnoseIfAttr *> that doesn't own the vector:
- /// If NumTriggeredDiagnoseIfs is 0 or 1, this is a DiagnoseIfAttr *,
- /// otherwise it's a pointer to an array of `NumTriggeredDiagnoseIfs`
- /// DiagnoseIfAttr *s.
- llvm::PointerUnion<DiagnoseIfAttr *, DiagnoseIfAttr **> DiagnoseIfInfo;
-
- /// Gets an ArrayRef for the data at DiagnoseIfInfo. Note that this may give
- /// you a pointer into DiagnoseIfInfo.
- ArrayRef<DiagnoseIfAttr *> getDiagnoseIfInfo() const {
- auto *Ptr = NumTriggeredDiagnoseIfs <= 1
- ? DiagnoseIfInfo.getAddrOfPtr1()
- : DiagnoseIfInfo.get<DiagnoseIfAttr **>();
- return {Ptr, NumTriggeredDiagnoseIfs};
- }
-
union {
DeductionFailureInfo DeductionFailure;
SmallVector<OverloadCandidate, 16> Candidates;
llvm::SmallPtrSet<Decl *, 16> Functions;
- // Allocator for 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;
/// 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.
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) {
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,
TemplateArgumentListInfo *ExplicitTemplateArgs,
QualType ObjectType,
Expr::Classification ObjectClassification,
- Expr *ThisArg,
ArrayRef<Expr *> Args,
OverloadCandidateSet& CandidateSet,
bool SuppressUserConversions = false,
EnableIfAttr *CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
bool MissingImplicitThis = false);
- /// Check the diagnose_if attributes on the given function. Returns the
- /// first succesful fatal attribute, or null if calling Function(Args) isn't
- /// an error.
+ /// 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.
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);
}
/// 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;
CheckArgumentWithTypeTag(I, Args.data());
}
}
+
+ if (FD)
+ diagnoseArgDependentDiagnoseIfAttrs(FD, ThisArg, Args, Loc);
}
/// CheckConstructorCall - Check a constructor call for correctness and safety
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
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);
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;
CallType = VariadicFunction;
}
- checkCall(NDecl, Proto,
+ checkCall(NDecl, Proto, /*ThisArg=*/nullptr,
llvm::makeArrayRef(TheCall->getArgs(), TheCall->getNumArgs()),
/*IsMemberFunction=*/false, TheCall->getRParenLoc(),
TheCall->getCallee()->getSourceRange(), CallType);
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);
}
// See if this is a deleted function.
- SmallVector<DiagnoseIfAttr *, 4> DiagnoseIfWarnings;
if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
if (FD->isDeleted()) {
auto *Ctor = dyn_cast<CXXConstructorDecl>(FD);
if (getLangOpts().CUDA && !CheckCUDACall(Loc, FD))
return true;
- if (const DiagnoseIfAttr *A =
- checkArgIndependentDiagnoseIf(FD, DiagnoseIfWarnings)) {
- emitDiagnoseIfDiagnostic(Loc, A);
+ if (diagnoseArgIndependentDiagnoseIfAttrs(FD, Loc))
return true;
- }
}
// [OpenMP 4.0], 2.15 declare reduction Directive, Restrictions
return true;
}
- for (const auto *W : DiagnoseIfWarnings)
- emitDiagnoseIfDiagnostic(Loc, W);
-
DiagnoseAvailabilityOfDecl(*this, D, Loc, UnknownObjCClass,
ObjCPropertyAccess);
<< 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.
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;
}
if (CXXMethodDecl *M = dyn_cast<CXXMethodDecl>(Cand->getUnderlyingDecl())) {
if (SM == CXXCopyAssignment || SM == CXXMoveAssignment)
AddMethodCandidate(M, Cand, RD, ThisTy, Classification,
- /*ThisArg=*/nullptr,
llvm::makeArrayRef(&Arg, NumArgs), OCS, true);
else if (CtorInfo)
AddOverloadCandidate(CtorInfo.Constructor, CtorInfo.FoundDecl,
if (SM == CXXCopyAssignment || SM == CXXMoveAssignment)
AddMethodTemplateCandidate(
Tmpl, Cand, RD, nullptr, ThisTy, Classification,
- /*ThisArg=*/nullptr, llvm::makeArrayRef(&Arg, NumArgs), OCS, true);
+ llvm::makeArrayRef(&Arg, NumArgs), OCS, true);
else if (CtorInfo)
AddTemplateOverloadCandidate(
CtorInfo.ConstructorTmpl, CtorInfo.FoundDecl, nullptr,
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 {
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
// 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
Candidate.FailureKind = ovl_fail_ext_disabled;
return;
}
-
- initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Function, Args);
}
ObjCMethodDecl *
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
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);
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(),
void Sema::AddMethodCandidate(DeclAccessPair FoundDecl,
QualType ObjectType,
Expr::Classification ObjectClassification,
- Expr *ThisArg,
ArrayRef<Expr *> Args,
OverloadCandidateSet& CandidateSet,
bool SuppressUserConversions) {
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);
}
}
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,
Candidate.DeductionFailure.Data = FailedAttr;
return;
}
-
- initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Method, Args,
- /*MissingImplicitThis=*/!ThisArg, ThisArg);
}
/// \brief Add a C++ member function template as a candidate to the candidate
TemplateArgumentListInfo *ExplicitTemplateArgs,
QualType ObjectType,
Expr::Classification ObjectClassification,
- Expr *ThisArg,
ArrayRef<Expr *> Args,
OverloadCandidateSet& CandidateSet,
bool SuppressUserConversions,
assert(isa<CXXMethodDecl>(Specialization) &&
"Specialization is not a member function?");
AddMethodCandidate(cast<CXXMethodDecl>(Specialization), FoundDecl,
- ActingContext, ObjectType, ObjectClassification,
- /*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
Candidate.DeductionFailure.Data = FailedAttr;
return;
}
-
- initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Conversion, None, false, From);
}
/// \brief Adds a conversion function template specialization
Candidate.DeductionFailure.Data = FailedAttr;
return;
}
-
- initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Conversion, None);
}
/// \brief Add overload candidates for overloaded operators that are
Oper != OperEnd;
++Oper)
AddMethodCandidate(Oper.getPair(), Args[0]->getType(),
- Args[0]->Classify(Context), Args[0], Args.slice(1),
+ Args[0]->Classify(Context), Args.slice(1),
CandidateSet, /*SuppressUserConversions=*/false);
}
}
}
}
-static bool isCandidateUnavailableDueToDiagnoseIf(const OverloadCandidate &OC) {
- ArrayRef<DiagnoseIfAttr *> Info = OC.getDiagnoseIfInfo();
- if (!Info.empty() && Info[0]->isError())
- return true;
-
- assert(llvm::all_of(Info,
- [](const DiagnoseIfAttr *A) { return !A->isError(); }) &&
- "DiagnoseIf info shouldn't have mixed warnings and errors.");
- return false;
-}
-
/// \brief Computes the best viable function (C++ 13.3.3)
/// within an overload candidate set.
///
// Best is the best viable function.
if (Best->Function &&
(Best->Function->isDeleted() ||
- S.isFunctionConsideredUnavailable(Best->Function) ||
- 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;
}
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);
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
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 {
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
TemplateArgs = &TemplateArgsBuffer;
}
- // Poor-programmer's Lazy<Expr *>; isImplicitAccess requires stripping
- // parens/casts, which would be nice to avoid potentially doing multiple
- // times.
- llvm::Optional<Expr *> UnresolvedBase;
- auto GetUnresolvedBase = [&] {
- if (!UnresolvedBase.hasValue())
- UnresolvedBase =
- UnresExpr->isImplicitAccess() ? nullptr : UnresExpr->getBase();
- return *UnresolvedBase;
- };
for (UnresolvedMemberExpr::decls_iterator I = UnresExpr->decls_begin(),
E = UnresExpr->decls_end(); I != E; ++I) {
continue;
AddMethodCandidate(Method, I.getPair(), ActingDC, ObjectType,
- ObjectClassification,
- /*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);
}
}
<< 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) ||
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:
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);
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);
}
#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}}
}
#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'}}
}
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}}
}
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}}
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");
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>'}}
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() {
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>
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();
void late() {
bar(); // expected-error{{bad foo}}
bar(0);
- bar(1); // expected-error{{call to unavailable function}}
+ bar(1); // expected-error{{bad 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, ""))) {
}
};
-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}}
}
}
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();
}
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}}
}
}
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 {
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}}
}
}
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}}
}
}