Index: cfe/trunk/include/clang/Sema/Overload.h =================================================================== --- cfe/trunk/include/clang/Sema/Overload.h +++ cfe/trunk/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 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 DiagnoseIfInfo; - - /// Gets an ArrayRef for the data at DiagnoseIfInfo. Note that this may give - /// you a pointer into DiagnoseIfInfo. - ArrayRef getDiagnoseIfInfo() const { - auto *Ptr = NumTriggeredDiagnoseIfs <= 1 - ? DiagnoseIfInfo.getAddrOfPtr1() - : DiagnoseIfInfo.get(); - return {Ptr, NumTriggeredDiagnoseIfs}; - } - union { DeductionFailureInfo DeductionFailure; @@ -759,9 +739,8 @@ SmallVector Candidates; llvm::SmallPtrSet 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 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 CA); - /// \brief Determine when this overload candidate will be new to the /// overload set. bool isNewCandidate(Decl *F) { Index: cfe/trunk/include/clang/Sema/Sema.h =================================================================== --- cfe/trunk/include/clang/Sema/Sema.h +++ cfe/trunk/include/clang/Sema/Sema.h @@ -2545,14 +2545,14 @@ void AddMethodCandidate(DeclAccessPair FoundDecl, QualType ObjectType, Expr::Classification ObjectClassification, - Expr *ThisArg, ArrayRef Args, + ArrayRef Args, OverloadCandidateSet& CandidateSet, bool SuppressUserConversion = false); void AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext, QualType ObjectType, Expr::Classification ObjectClassification, - Expr *ThisArg, ArrayRef Args, + ArrayRef Args, OverloadCandidateSet& CandidateSet, bool SuppressUserConversions = false, bool PartialOverloading = false, @@ -2563,7 +2563,6 @@ TemplateArgumentListInfo *ExplicitTemplateArgs, QualType ObjectType, Expr::Classification ObjectClassification, - Expr *ThisArg, ArrayRef Args, OverloadCandidateSet& CandidateSet, bool SuppressUserConversions = false, @@ -2637,37 +2636,27 @@ EnableIfAttr *CheckEnableIf(FunctionDecl *Function, ArrayRef Args, bool MissingImplicitThis = false); - /// Check the diagnose_if attributes on the given function. Returns the - /// first succesful fatal attribute, or null if calling Function(Args) isn't - /// an error. - /// - /// This only considers ArgDependent DiagnoseIfAttrs. - /// - /// This will populate Nonfatal with all non-error DiagnoseIfAttrs that - /// succeed. If this function returns non-null, the contents of Nonfatal are - /// unspecified. - DiagnoseIfAttr * - checkArgDependentDiagnoseIf(FunctionDecl *Function, ArrayRef Args, - SmallVectorImpl &Nonfatal, - bool MissingImplicitThis = false, - Expr *ThisArg = nullptr); - - /// Check the diagnose_if expressions on the given function. Returns the - /// first succesful fatal attribute, or null if using Function isn't - /// an error. - /// - /// This ignores all ArgDependent DiagnoseIfAttrs. - /// - /// This will populate Nonfatal with all non-error DiagnoseIfAttrs that - /// succeed. If this function returns non-null, the contents of Nonfatal are - /// unspecified. - DiagnoseIfAttr * - checkArgIndependentDiagnoseIf(FunctionDecl *Function, - SmallVectorImpl &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); + /// Emit diagnostics for the diagnose_if attributes on Function, ignoring any + /// non-ArgDependent DiagnoseIfAttrs. + /// + /// Argument-dependent diagnose_if attributes should be checked each time a + /// function is used as a direct callee of a function call. + /// + /// Returns true if any errors were emitted. + bool diagnoseArgDependentDiagnoseIfAttrs(const FunctionDecl *Function, + const Expr *ThisArg, + ArrayRef Args, + SourceLocation Loc); + + /// Emit diagnostics for the diagnose_if attributes on Function, ignoring any + /// ArgDependent DiagnoseIfAttrs. + /// + /// Argument-independent diagnose_if attributes should be checked on every use + /// of a function. + /// + /// 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 Args, bool IsMemberFunction, - SourceLocation Loc, SourceRange Range, + const Expr *ThisArg, ArrayRef Args, + bool IsMemberFunction, SourceLocation Loc, SourceRange Range, VariadicCallType CallType); bool CheckObjCString(Expr *Arg); Index: cfe/trunk/lib/Sema/SemaChecking.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaChecking.cpp +++ cfe/trunk/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 Args, bool IsMemberFunction, - SourceLocation Loc, SourceRange Range, - VariadicCallType CallType) { + const Expr *ThisArg, ArrayRef 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(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); Index: cfe/trunk/lib/Sema/SemaExpr.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaExpr.cpp +++ cfe/trunk/lib/Sema/SemaExpr.cpp @@ -342,7 +342,6 @@ } // See if this is a deleted function. - SmallVector DiagnoseIfWarnings; if (FunctionDecl *FD = dyn_cast(D)) { if (FD->isDeleted()) { auto *Ctor = dyn_cast(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 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. Index: cfe/trunk/lib/Sema/SemaExprCXX.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaExprCXX.cpp +++ cfe/trunk/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())) + return ExprError(); + return CE; } Index: cfe/trunk/lib/Sema/SemaLookup.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaLookup.cpp +++ cfe/trunk/lib/Sema/SemaLookup.cpp @@ -2961,7 +2961,6 @@ if (CXXMethodDecl *M = dyn_cast(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, Index: cfe/trunk/lib/Sema/SemaOverload.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaOverload.cpp +++ cfe/trunk/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 CA) { - auto *DIA = slabAllocate(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 Args, - bool MissingImplicitThis = false, - Expr *ExplicitThis = nullptr) { - SmallVector 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 &Errors, - SmallVectorImpl &Nonfatal) { - for (auto *DIA : Function->specific_attrs()) - if (ArgDependent == DIA->getArgDependent()) { - if (DIA->isError()) - Errors.push_back(DIA); - else - Nonfatal.push_back(DIA); - } - - return !Errors.empty() || !Nonfatal.empty(); -} - template -static DiagnoseIfAttr * -checkDiagnoseIfAttrsWith(const SmallVectorImpl &Errors, - SmallVectorImpl &Nonfatal, - CheckFn &&IsSuccessful) { +static bool diagnoseDiagnoseIfAttrsWith(Sema &S, const FunctionDecl *FD, + bool ArgDependent, SourceLocation Loc, + CheckFn &&IsSuccessful) { + SmallVector Attrs; + for (const auto *DIA : FD->specific_attrs()) { + 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; - - llvm::erase_if(Nonfatal, [&](DiagnoseIfAttr *A) { return !IsSuccessful(A); }); - return nullptr; -} - -DiagnoseIfAttr * -Sema::checkArgDependentDiagnoseIf(FunctionDecl *Function, ArrayRef Args, - SmallVectorImpl &Nonfatal, - bool MissingImplicitThis, - Expr *ThisArg) { - SmallVector 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 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 &Nonfatal) { - SmallVector 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 Args, + SourceLocation Loc) { + return diagnoseDiagnoseIfAttrsWith( + *this, Function, /*ArgDependent=*/true, Loc, + [&](const DiagnoseIfAttr *DIA) { + APValue Result; + // It's sane to use the same Args for any redecl of this function, since + // EvaluateWithSubstitution only cares about the position of each + // argument in the arg list, not the ParmVarDecl* it maps to. + if (!DIA->getCond()->EvaluateWithSubstitution( + Result, Context, DIA->getParent(), Args, ThisArg)) + return false; + return Result.isInt() && Result.getInt().getBoolValue(); + }); } -void Sema::emitDiagnoseIfDiagnostic(SourceLocation Loc, - const DiagnoseIfAttr *DIA) { - auto Code = DIA->isError() ? diag::err_diagnose_if_succeeded - : diag::warn_diagnose_if_succeeded; - Diag(Loc, Code) << DIA->getMessage(); - Diag(DIA->getLocation(), diag::note_from_diagnose_if) - << DIA->getParent() << DIA->getCond()->getSourceRange(); +bool Sema::diagnoseArgIndependentDiagnoseIfAttrs(const FunctionDecl *Function, + SourceLocation Loc) { + return diagnoseDiagnoseIfAttrsWith( + *this, Function, /*ArgDependent=*/false, Loc, + [&](const DiagnoseIfAttr *DIA) { + bool Result; + return DIA->getCond()->EvaluateAsBooleanCondition(Result, Context) && + Result; + }); } /// \brief Add all of the function declarations in the given function set to @@ -6356,8 +6311,8 @@ AddMethodCandidate(cast(FD), F.getPair(), cast(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(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 Args, OverloadCandidateSet& CandidateSet, bool SuppressUserConversions) { @@ -6399,15 +6353,13 @@ assert(isa(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(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 Args, + ArrayRef 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 Args, OverloadCandidateSet& CandidateSet, bool SuppressUserConversions, @@ -6613,9 +6561,9 @@ assert(isa(Specialization) && "Specialization is not a member function?"); AddMethodCandidate(cast(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 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(); - 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())) + return ExprError(); + return MaybeBindToTemporary(TheCall); } else { // We matched a built-in operator. Convert the arguments, then @@ -12343,16 +12266,20 @@ return ExprError(); ArrayRef ArgsArray(Args, 2); + const Expr *ImplicitThis = nullptr; // Cut off the implicit 'this'. - if (isa(FnDecl)) + if (isa(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(FnDecl), OpLoc, - TheCall->getSourceRange(), VariadicDoesNotApply); + checkCall(FnDecl, nullptr, ImplicitThis, ArgsArray, + isa(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())) + 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; isImplicitAccess requires stripping - // parens/casts, which would be nice to avoid potentially doing multiple - // times. - llvm::Optional 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(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 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(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())) + return ExprError(); return MaybeBindToTemporary(TheCall); } Index: cfe/trunk/test/Sema/diagnose_if.c =================================================================== --- cfe/trunk/test/Sema/diagnose_if.c +++ cfe/trunk/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}} } Index: cfe/trunk/test/SemaCXX/diagnose_if.cpp =================================================================== --- cfe/trunk/test/SemaCXX/diagnose_if.cpp +++ cfe/trunk/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 void neverok() _diagnose_if(!T(), "oh no", "error") {} // expected-note 4{{from 'diagnose_if'}} @@ -51,14 +53,14 @@ } template -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 -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 -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 -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 ''}} @@ -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().bar(1); // expected-error{{bad i}} Errors().fooOvl(0); - Errors().fooOvl(1); // expected-error{{call to unavailable}} + Errors().fooOvl(1); // expected-error{{int bad i}} Errors().fooOvl(short(0)); - Errors().fooOvl(short(1)); // expected-error{{call to unavailable}} + Errors().fooOvl(short(1)); // expected-error{{short bad i}} Errors().barOvl(0); - Errors().barOvl(1); // expected-error{{call to unavailable}} + Errors().barOvl(1); // expected-error{{int bad i}} Errors().barOvl(short(0)); - Errors().barOvl(short(1)); // expected-error{{call to unavailable}} + Errors().barOvl(short(1)); // expected-error{{short bad i}} } template @@ -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 T getVal() _diagnose_if(bad(), "oh no", "error") { // expected-note{{oh no}} + template T getVal() _diagnose_if(bad(), "oh no", "error") { // expected-note{{from 'diagnose_if'}} return T(); } template - 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 - 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(); - Foo(1).getVal(); // expected-error{{call to unavailable member function}} + Foo(1).getVal(); // expected-error{{oh no}} Foo(0).getVal2(); - Foo(1).getVal2(); // expected-error{{call to unavailable member function}} + Foo(1).getVal2(); // 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 - 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 + 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 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{}}; + Foo{std::initializer_list{1}}; // expected-warning{{oh no}} + Foo{std::initializer_list{1, 2}}; // expected-error{{oh no}} + Foo{std::initializer_list{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}} } }