diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index ab3cbd19e1ff01..ef4fc47567a7c4 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2114,8 +2114,6 @@ class Sema final : public SemaBase { bool FormatStringHasSArg(const StringLiteral *FExpr); - static bool GetFormatNSStringIdx(const FormatAttr *Format, unsigned &Idx); - void CheckFloatComparison(SourceLocation Loc, Expr *LHS, Expr *RHS, BinaryOperatorKind Opcode); @@ -2228,8 +2226,6 @@ class Sema final : public SemaBase { bool BuiltinVectorMath(CallExpr *TheCall, QualType &Res); bool BuiltinVectorToScalarMath(CallExpr *TheCall); - bool CheckHLSLBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall); - void checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto, const Expr *ThisArg, ArrayRef Args, bool IsMemberFunction, SourceLocation Loc, SourceRange Range, @@ -2259,6 +2255,14 @@ class Sema final : public SemaBase { bool ValueIsRunOfOnes(CallExpr *TheCall, unsigned ArgNum); + void CheckImplicitConversion(Expr *E, QualType T, SourceLocation CC, + bool *ICContext = nullptr, + bool IsListInit = false); + + bool BuiltinElementwiseTernaryMath(CallExpr *TheCall, + bool CheckForFloatArgs = true); + bool PrepareBuiltinElementwiseMathOneArgCall(CallExpr *TheCall); + private: void CheckArrayAccess(const Expr *BaseExpr, const Expr *IndexExpr, const ArraySubscriptExpr *ASE = nullptr, @@ -2306,9 +2310,6 @@ class Sema final : public SemaBase { AtomicExpr::AtomicOp Op); bool BuiltinElementwiseMath(CallExpr *TheCall); - bool BuiltinElementwiseTernaryMath(CallExpr *TheCall, - bool CheckForFloatArgs = true); - bool PrepareBuiltinElementwiseMathOneArgCall(CallExpr *TheCall); bool PrepareBuiltinReduceMathOneArgCall(CallExpr *TheCall); bool BuiltinNonDeterministicValue(CallExpr *TheCall); diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h index 0e41a72e444ef4..4d6958a1be3e5b 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -58,6 +58,8 @@ class SemaHLSL : public SemaBase { void handleShaderAttr(Decl *D, const ParsedAttr &AL); void handleResourceBindingAttr(Decl *D, const ParsedAttr &AL); void handleParamModifierAttr(Decl *D, const ParsedAttr &AL); + + bool CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall); }; } // namespace clang diff --git a/clang/include/clang/Sema/SemaObjC.h b/clang/include/clang/Sema/SemaObjC.h index bb8887691ce5d3..07c3c1a06be160 100644 --- a/clang/include/clang/Sema/SemaObjC.h +++ b/clang/include/clang/Sema/SemaObjC.h @@ -158,6 +158,27 @@ class SemaObjC : public SemaBase { IdentifierInfo *getNSErrorIdent(); + bool GetFormatNSStringIdx(const FormatAttr *Format, unsigned &Idx); + + /// Diagnose use of %s directive in an NSString which is being passed + /// as formatting string to formatting method. + void DiagnoseCStringFormatDirectiveInCFAPI(const NamedDecl *FDecl, + Expr **Args, unsigned NumArgs); + + bool isSignedCharBool(QualType Ty); + + void adornBoolConversionDiagWithTernaryFixit( + Expr *SourceExpr, const Sema::SemaDiagnosticBuilder &Builder); + + /// Check an Objective-C dictionary literal being converted to the given + /// target type. + void checkDictionaryLiteral(QualType TargetType, + ObjCDictionaryLiteral *DictionaryLiteral); + + /// Check an Objective-C array literal being converted to the given + /// target type. + void checkArrayLiteral(QualType TargetType, ObjCArrayLiteral *ArrayLiteral); + private: IdentifierInfo *Ident_NSError = nullptr; diff --git a/clang/include/clang/Sema/SemaOpenCL.h b/clang/include/clang/Sema/SemaOpenCL.h index 0d80c4b4c0b561..7d6b4b0dec09c2 100644 --- a/clang/include/clang/Sema/SemaOpenCL.h +++ b/clang/include/clang/Sema/SemaOpenCL.h @@ -28,6 +28,78 @@ class SemaOpenCL : public SemaBase { // Handles intel_reqd_sub_group_size. void handleSubGroupSize(Decl *D, const ParsedAttr &AL); + + // Performs semantic analysis for the read/write_pipe call. + // \param S Reference to the semantic analyzer. + // \param Call A pointer to the builtin call. + // \return True if a semantic error has been found, false otherwise. + bool checkBuiltinRWPipe(CallExpr *Call); + + // Performs a semantic analysis on the {work_group_/sub_group_ + // /_}reserve_{read/write}_pipe + // \param S Reference to the semantic analyzer. + // \param Call The call to the builtin function to be analyzed. + // \return True if a semantic error was found, false otherwise. + bool checkBuiltinReserveRWPipe(CallExpr *Call); + + bool checkSubgroupExt(CallExpr *Call); + + // Performs a semantic analysis on {work_group_/sub_group_ + // /_}commit_{read/write}_pipe + // \param S Reference to the semantic analyzer. + // \param Call The call to the builtin function to be analyzed. + // \return True if a semantic error was found, false otherwise. + bool checkBuiltinCommitRWPipe(CallExpr *Call); + + // Performs a semantic analysis on the call to built-in Pipe + // Query Functions. + // \param S Reference to the semantic analyzer. + // \param Call The call to the builtin function to be analyzed. + // \return True if a semantic error was found, false otherwise. + bool checkBuiltinPipePackets(CallExpr *Call); + + // OpenCL v2.0 s6.13.9 - Address space qualifier functions. + // Performs semantic analysis for the to_global/local/private call. + // \param S Reference to the semantic analyzer. + // \param BuiltinID ID of the builtin function. + // \param Call A pointer to the builtin call. + // \return True if a semantic error has been found, false otherwise. + bool checkBuiltinToAddr(unsigned BuiltinID, CallExpr *Call); + + /// OpenCL C v2.0, s6.13.17 - Enqueue kernel function contains four different + /// overload formats specified in Table 6.13.17.1. + /// int enqueue_kernel(queue_t queue, + /// kernel_enqueue_flags_t flags, + /// const ndrange_t ndrange, + /// void (^block)(void)) + /// int enqueue_kernel(queue_t queue, + /// kernel_enqueue_flags_t flags, + /// const ndrange_t ndrange, + /// uint num_events_in_wait_list, + /// clk_event_t *event_wait_list, + /// clk_event_t *event_ret, + /// void (^block)(void)) + /// int enqueue_kernel(queue_t queue, + /// kernel_enqueue_flags_t flags, + /// const ndrange_t ndrange, + /// void (^block)(local void*, ...), + /// uint size0, ...) + /// int enqueue_kernel(queue_t queue, + /// kernel_enqueue_flags_t flags, + /// const ndrange_t ndrange, + /// uint num_events_in_wait_list, + /// clk_event_t *event_wait_list, + /// clk_event_t *event_ret, + /// void (^block)(local void*, ...), + /// uint size0, ...) + bool checkBuiltinEnqueueKernel(CallExpr *TheCall); + + /// OpenCL C v2.0, s6.13.17.6 - Check the argument to the + /// get_kernel_work_group_size + /// and get_kernel_preferred_work_group_size_multiple builtin functions. + bool checkBuiltinKernelWorkGroupSize(CallExpr *TheCall); + + bool checkBuiltinNDRangeAndBlock(CallExpr *TheCall); }; } // namespace clang diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index bce941cc00e0ed..005fbfd42a8ab1 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -64,12 +64,14 @@ #include "clang/Sema/SemaAMDGPU.h" #include "clang/Sema/SemaARM.h" #include "clang/Sema/SemaBPF.h" +#include "clang/Sema/SemaHLSL.h" #include "clang/Sema/SemaHexagon.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/SemaLoongArch.h" #include "clang/Sema/SemaMIPS.h" #include "clang/Sema/SemaNVPTX.h" #include "clang/Sema/SemaObjC.h" +#include "clang/Sema/SemaOpenCL.h" #include "clang/Sema/SemaPPC.h" #include "clang/Sema/SemaRISCV.h" #include "clang/Sema/SemaSystemZ.h" @@ -1486,528 +1488,6 @@ static bool BuiltinSEHScopeCheck(Sema &SemaRef, CallExpr *TheCall, return false; } -static inline bool isBlockPointer(Expr *Arg) { - return Arg->getType()->isBlockPointerType(); -} - -/// OpenCL C v2.0, s6.13.17.2 - Checks that the block parameters are all local -/// void*, which is a requirement of device side enqueue. -static bool checkOpenCLBlockArgs(Sema &S, Expr *BlockArg) { - const BlockPointerType *BPT = - cast(BlockArg->getType().getCanonicalType()); - ArrayRef Params = - BPT->getPointeeType()->castAs()->getParamTypes(); - unsigned ArgCounter = 0; - bool IllegalParams = false; - // Iterate through the block parameters until either one is found that is not - // a local void*, or the block is valid. - for (ArrayRef::iterator I = Params.begin(), E = Params.end(); - I != E; ++I, ++ArgCounter) { - if (!(*I)->isPointerType() || !(*I)->getPointeeType()->isVoidType() || - (*I)->getPointeeType().getQualifiers().getAddressSpace() != - LangAS::opencl_local) { - // Get the location of the error. If a block literal has been passed - // (BlockExpr) then we can point straight to the offending argument, - // else we just point to the variable reference. - SourceLocation ErrorLoc; - if (isa(BlockArg)) { - BlockDecl *BD = cast(BlockArg)->getBlockDecl(); - ErrorLoc = BD->getParamDecl(ArgCounter)->getBeginLoc(); - } else if (isa(BlockArg)) { - ErrorLoc = cast(BlockArg)->getBeginLoc(); - } - S.Diag(ErrorLoc, - diag::err_opencl_enqueue_kernel_blocks_non_local_void_args); - IllegalParams = true; - } - } - - return IllegalParams; -} - -static bool checkOpenCLSubgroupExt(Sema &S, CallExpr *Call) { - // OpenCL device can support extension but not the feature as extension - // requires subgroup independent forward progress, but subgroup independent - // forward progress is optional in OpenCL C 3.0 __opencl_c_subgroups feature. - if (!S.getOpenCLOptions().isSupported("cl_khr_subgroups", S.getLangOpts()) && - !S.getOpenCLOptions().isSupported("__opencl_c_subgroups", - S.getLangOpts())) { - S.Diag(Call->getBeginLoc(), diag::err_opencl_requires_extension) - << 1 << Call->getDirectCallee() - << "cl_khr_subgroups or __opencl_c_subgroups"; - return true; - } - return false; -} - -static bool OpenCLBuiltinNDRangeAndBlock(Sema &S, CallExpr *TheCall) { - if (S.checkArgCount(TheCall, 2)) - return true; - - if (checkOpenCLSubgroupExt(S, TheCall)) - return true; - - // First argument is an ndrange_t type. - Expr *NDRangeArg = TheCall->getArg(0); - if (NDRangeArg->getType().getUnqualifiedType().getAsString() != "ndrange_t") { - S.Diag(NDRangeArg->getBeginLoc(), diag::err_opencl_builtin_expected_type) - << TheCall->getDirectCallee() << "'ndrange_t'"; - return true; - } - - Expr *BlockArg = TheCall->getArg(1); - if (!isBlockPointer(BlockArg)) { - S.Diag(BlockArg->getBeginLoc(), diag::err_opencl_builtin_expected_type) - << TheCall->getDirectCallee() << "block"; - return true; - } - return checkOpenCLBlockArgs(S, BlockArg); -} - -/// OpenCL C v2.0, s6.13.17.6 - Check the argument to the -/// get_kernel_work_group_size -/// and get_kernel_preferred_work_group_size_multiple builtin functions. -static bool OpenCLBuiltinKernelWorkGroupSize(Sema &S, CallExpr *TheCall) { - if (S.checkArgCount(TheCall, 1)) - return true; - - Expr *BlockArg = TheCall->getArg(0); - if (!isBlockPointer(BlockArg)) { - S.Diag(BlockArg->getBeginLoc(), diag::err_opencl_builtin_expected_type) - << TheCall->getDirectCallee() << "block"; - return true; - } - return checkOpenCLBlockArgs(S, BlockArg); -} - -/// Diagnose integer type and any valid implicit conversion to it. -static bool checkOpenCLEnqueueIntType(Sema &S, Expr *E, - const QualType &IntType); - -static bool checkOpenCLEnqueueLocalSizeArgs(Sema &S, CallExpr *TheCall, - unsigned Start, unsigned End) { - bool IllegalParams = false; - for (unsigned I = Start; I <= End; ++I) - IllegalParams |= checkOpenCLEnqueueIntType(S, TheCall->getArg(I), - S.Context.getSizeType()); - return IllegalParams; -} - -/// OpenCL v2.0, s6.13.17.1 - Check that sizes are provided for all -/// 'local void*' parameter of passed block. -static bool checkOpenCLEnqueueVariadicArgs(Sema &S, CallExpr *TheCall, - Expr *BlockArg, - unsigned NumNonVarArgs) { - const BlockPointerType *BPT = - cast(BlockArg->getType().getCanonicalType()); - unsigned NumBlockParams = - BPT->getPointeeType()->castAs()->getNumParams(); - unsigned TotalNumArgs = TheCall->getNumArgs(); - - // For each argument passed to the block, a corresponding uint needs to - // be passed to describe the size of the local memory. - if (TotalNumArgs != NumBlockParams + NumNonVarArgs) { - S.Diag(TheCall->getBeginLoc(), - diag::err_opencl_enqueue_kernel_local_size_args); - return true; - } - - // Check that the sizes of the local memory are specified by integers. - return checkOpenCLEnqueueLocalSizeArgs(S, TheCall, NumNonVarArgs, - TotalNumArgs - 1); -} - -/// OpenCL C v2.0, s6.13.17 - Enqueue kernel function contains four different -/// overload formats specified in Table 6.13.17.1. -/// int enqueue_kernel(queue_t queue, -/// kernel_enqueue_flags_t flags, -/// const ndrange_t ndrange, -/// void (^block)(void)) -/// int enqueue_kernel(queue_t queue, -/// kernel_enqueue_flags_t flags, -/// const ndrange_t ndrange, -/// uint num_events_in_wait_list, -/// clk_event_t *event_wait_list, -/// clk_event_t *event_ret, -/// void (^block)(void)) -/// int enqueue_kernel(queue_t queue, -/// kernel_enqueue_flags_t flags, -/// const ndrange_t ndrange, -/// void (^block)(local void*, ...), -/// uint size0, ...) -/// int enqueue_kernel(queue_t queue, -/// kernel_enqueue_flags_t flags, -/// const ndrange_t ndrange, -/// uint num_events_in_wait_list, -/// clk_event_t *event_wait_list, -/// clk_event_t *event_ret, -/// void (^block)(local void*, ...), -/// uint size0, ...) -static bool OpenCLBuiltinEnqueueKernel(Sema &S, CallExpr *TheCall) { - unsigned NumArgs = TheCall->getNumArgs(); - - if (NumArgs < 4) { - S.Diag(TheCall->getBeginLoc(), - diag::err_typecheck_call_too_few_args_at_least) - << 0 << 4 << NumArgs << /*is non object*/ 0; - return true; - } - - Expr *Arg0 = TheCall->getArg(0); - Expr *Arg1 = TheCall->getArg(1); - Expr *Arg2 = TheCall->getArg(2); - Expr *Arg3 = TheCall->getArg(3); - - // First argument always needs to be a queue_t type. - if (!Arg0->getType()->isQueueT()) { - S.Diag(TheCall->getArg(0)->getBeginLoc(), - diag::err_opencl_builtin_expected_type) - << TheCall->getDirectCallee() << S.Context.OCLQueueTy; - return true; - } - - // Second argument always needs to be a kernel_enqueue_flags_t enum value. - if (!Arg1->getType()->isIntegerType()) { - S.Diag(TheCall->getArg(1)->getBeginLoc(), - diag::err_opencl_builtin_expected_type) - << TheCall->getDirectCallee() << "'kernel_enqueue_flags_t' (i.e. uint)"; - return true; - } - - // Third argument is always an ndrange_t type. - if (Arg2->getType().getUnqualifiedType().getAsString() != "ndrange_t") { - S.Diag(TheCall->getArg(2)->getBeginLoc(), - diag::err_opencl_builtin_expected_type) - << TheCall->getDirectCallee() << "'ndrange_t'"; - return true; - } - - // With four arguments, there is only one form that the function could be - // called in: no events and no variable arguments. - if (NumArgs == 4) { - // check that the last argument is the right block type. - if (!isBlockPointer(Arg3)) { - S.Diag(Arg3->getBeginLoc(), diag::err_opencl_builtin_expected_type) - << TheCall->getDirectCallee() << "block"; - return true; - } - // we have a block type, check the prototype - const BlockPointerType *BPT = - cast(Arg3->getType().getCanonicalType()); - if (BPT->getPointeeType()->castAs()->getNumParams() > 0) { - S.Diag(Arg3->getBeginLoc(), - diag::err_opencl_enqueue_kernel_blocks_no_args); - return true; - } - return false; - } - // we can have block + varargs. - if (isBlockPointer(Arg3)) - return (checkOpenCLBlockArgs(S, Arg3) || - checkOpenCLEnqueueVariadicArgs(S, TheCall, Arg3, 4)); - // last two cases with either exactly 7 args or 7 args and varargs. - if (NumArgs >= 7) { - // check common block argument. - Expr *Arg6 = TheCall->getArg(6); - if (!isBlockPointer(Arg6)) { - S.Diag(Arg6->getBeginLoc(), diag::err_opencl_builtin_expected_type) - << TheCall->getDirectCallee() << "block"; - return true; - } - if (checkOpenCLBlockArgs(S, Arg6)) - return true; - - // Forth argument has to be any integer type. - if (!Arg3->getType()->isIntegerType()) { - S.Diag(TheCall->getArg(3)->getBeginLoc(), - diag::err_opencl_builtin_expected_type) - << TheCall->getDirectCallee() << "integer"; - return true; - } - // check remaining common arguments. - Expr *Arg4 = TheCall->getArg(4); - Expr *Arg5 = TheCall->getArg(5); - - // Fifth argument is always passed as a pointer to clk_event_t. - if (!Arg4->isNullPointerConstant(S.Context, - Expr::NPC_ValueDependentIsNotNull) && - !Arg4->getType()->getPointeeOrArrayElementType()->isClkEventT()) { - S.Diag(TheCall->getArg(4)->getBeginLoc(), - diag::err_opencl_builtin_expected_type) - << TheCall->getDirectCallee() - << S.Context.getPointerType(S.Context.OCLClkEventTy); - return true; - } - - // Sixth argument is always passed as a pointer to clk_event_t. - if (!Arg5->isNullPointerConstant(S.Context, - Expr::NPC_ValueDependentIsNotNull) && - !(Arg5->getType()->isPointerType() && - Arg5->getType()->getPointeeType()->isClkEventT())) { - S.Diag(TheCall->getArg(5)->getBeginLoc(), - diag::err_opencl_builtin_expected_type) - << TheCall->getDirectCallee() - << S.Context.getPointerType(S.Context.OCLClkEventTy); - return true; - } - - if (NumArgs == 7) - return false; - - return checkOpenCLEnqueueVariadicArgs(S, TheCall, Arg6, 7); - } - - // None of the specific case has been detected, give generic error - S.Diag(TheCall->getBeginLoc(), - diag::err_opencl_enqueue_kernel_incorrect_args); - return true; -} - -/// Returns OpenCL access qual. -static OpenCLAccessAttr *getOpenCLArgAccess(const Decl *D) { - return D->getAttr(); -} - -/// Returns true if pipe element type is different from the pointer. -static bool checkOpenCLPipeArg(Sema &S, CallExpr *Call) { - const Expr *Arg0 = Call->getArg(0); - // First argument type should always be pipe. - if (!Arg0->getType()->isPipeType()) { - S.Diag(Call->getBeginLoc(), diag::err_opencl_builtin_pipe_first_arg) - << Call->getDirectCallee() << Arg0->getSourceRange(); - return true; - } - OpenCLAccessAttr *AccessQual = - getOpenCLArgAccess(cast(Arg0)->getDecl()); - // Validates the access qualifier is compatible with the call. - // OpenCL v2.0 s6.13.16 - The access qualifiers for pipe should only be - // read_only and write_only, and assumed to be read_only if no qualifier is - // specified. - switch (Call->getDirectCallee()->getBuiltinID()) { - case Builtin::BIread_pipe: - case Builtin::BIreserve_read_pipe: - case Builtin::BIcommit_read_pipe: - case Builtin::BIwork_group_reserve_read_pipe: - case Builtin::BIsub_group_reserve_read_pipe: - case Builtin::BIwork_group_commit_read_pipe: - case Builtin::BIsub_group_commit_read_pipe: - if (!(!AccessQual || AccessQual->isReadOnly())) { - S.Diag(Arg0->getBeginLoc(), - diag::err_opencl_builtin_pipe_invalid_access_modifier) - << "read_only" << Arg0->getSourceRange(); - return true; - } - break; - case Builtin::BIwrite_pipe: - case Builtin::BIreserve_write_pipe: - case Builtin::BIcommit_write_pipe: - case Builtin::BIwork_group_reserve_write_pipe: - case Builtin::BIsub_group_reserve_write_pipe: - case Builtin::BIwork_group_commit_write_pipe: - case Builtin::BIsub_group_commit_write_pipe: - if (!(AccessQual && AccessQual->isWriteOnly())) { - S.Diag(Arg0->getBeginLoc(), - diag::err_opencl_builtin_pipe_invalid_access_modifier) - << "write_only" << Arg0->getSourceRange(); - return true; - } - break; - default: - break; - } - return false; -} - -/// Returns true if pipe element type is different from the pointer. -static bool checkOpenCLPipePacketType(Sema &S, CallExpr *Call, unsigned Idx) { - const Expr *Arg0 = Call->getArg(0); - const Expr *ArgIdx = Call->getArg(Idx); - const PipeType *PipeTy = cast(Arg0->getType()); - const QualType EltTy = PipeTy->getElementType(); - const PointerType *ArgTy = ArgIdx->getType()->getAs(); - // The Idx argument should be a pointer and the type of the pointer and - // the type of pipe element should also be the same. - if (!ArgTy || - !S.Context.hasSameType( - EltTy, ArgTy->getPointeeType()->getCanonicalTypeInternal())) { - S.Diag(Call->getBeginLoc(), diag::err_opencl_builtin_pipe_invalid_arg) - << Call->getDirectCallee() << S.Context.getPointerType(EltTy) - << ArgIdx->getType() << ArgIdx->getSourceRange(); - return true; - } - return false; -} - -// Performs semantic analysis for the read/write_pipe call. -// \param S Reference to the semantic analyzer. -// \param Call A pointer to the builtin call. -// \return True if a semantic error has been found, false otherwise. -static bool BuiltinRWPipe(Sema &S, CallExpr *Call) { - // OpenCL v2.0 s6.13.16.2 - The built-in read/write - // functions have two forms. - switch (Call->getNumArgs()) { - case 2: - if (checkOpenCLPipeArg(S, Call)) - return true; - // The call with 2 arguments should be - // read/write_pipe(pipe T, T*). - // Check packet type T. - if (checkOpenCLPipePacketType(S, Call, 1)) - return true; - break; - - case 4: { - if (checkOpenCLPipeArg(S, Call)) - return true; - // The call with 4 arguments should be - // read/write_pipe(pipe T, reserve_id_t, uint, T*). - // Check reserve_id_t. - if (!Call->getArg(1)->getType()->isReserveIDT()) { - S.Diag(Call->getBeginLoc(), diag::err_opencl_builtin_pipe_invalid_arg) - << Call->getDirectCallee() << S.Context.OCLReserveIDTy - << Call->getArg(1)->getType() << Call->getArg(1)->getSourceRange(); - return true; - } - - // Check the index. - const Expr *Arg2 = Call->getArg(2); - if (!Arg2->getType()->isIntegerType() && - !Arg2->getType()->isUnsignedIntegerType()) { - S.Diag(Call->getBeginLoc(), diag::err_opencl_builtin_pipe_invalid_arg) - << Call->getDirectCallee() << S.Context.UnsignedIntTy - << Arg2->getType() << Arg2->getSourceRange(); - return true; - } - - // Check packet type T. - if (checkOpenCLPipePacketType(S, Call, 3)) - return true; - } break; - default: - S.Diag(Call->getBeginLoc(), diag::err_opencl_builtin_pipe_arg_num) - << Call->getDirectCallee() << Call->getSourceRange(); - return true; - } - - return false; -} - -// Performs a semantic analysis on the {work_group_/sub_group_ -// /_}reserve_{read/write}_pipe -// \param S Reference to the semantic analyzer. -// \param Call The call to the builtin function to be analyzed. -// \return True if a semantic error was found, false otherwise. -static bool BuiltinReserveRWPipe(Sema &S, CallExpr *Call) { - if (S.checkArgCount(Call, 2)) - return true; - - if (checkOpenCLPipeArg(S, Call)) - return true; - - // Check the reserve size. - if (!Call->getArg(1)->getType()->isIntegerType() && - !Call->getArg(1)->getType()->isUnsignedIntegerType()) { - S.Diag(Call->getBeginLoc(), diag::err_opencl_builtin_pipe_invalid_arg) - << Call->getDirectCallee() << S.Context.UnsignedIntTy - << Call->getArg(1)->getType() << Call->getArg(1)->getSourceRange(); - return true; - } - - // Since return type of reserve_read/write_pipe built-in function is - // reserve_id_t, which is not defined in the builtin def file , we used int - // as return type and need to override the return type of these functions. - Call->setType(S.Context.OCLReserveIDTy); - - return false; -} - -// Performs a semantic analysis on {work_group_/sub_group_ -// /_}commit_{read/write}_pipe -// \param S Reference to the semantic analyzer. -// \param Call The call to the builtin function to be analyzed. -// \return True if a semantic error was found, false otherwise. -static bool BuiltinCommitRWPipe(Sema &S, CallExpr *Call) { - if (S.checkArgCount(Call, 2)) - return true; - - if (checkOpenCLPipeArg(S, Call)) - return true; - - // Check reserve_id_t. - if (!Call->getArg(1)->getType()->isReserveIDT()) { - S.Diag(Call->getBeginLoc(), diag::err_opencl_builtin_pipe_invalid_arg) - << Call->getDirectCallee() << S.Context.OCLReserveIDTy - << Call->getArg(1)->getType() << Call->getArg(1)->getSourceRange(); - return true; - } - - return false; -} - -// Performs a semantic analysis on the call to built-in Pipe -// Query Functions. -// \param S Reference to the semantic analyzer. -// \param Call The call to the builtin function to be analyzed. -// \return True if a semantic error was found, false otherwise. -static bool BuiltinPipePackets(Sema &S, CallExpr *Call) { - if (S.checkArgCount(Call, 1)) - return true; - - if (!Call->getArg(0)->getType()->isPipeType()) { - S.Diag(Call->getBeginLoc(), diag::err_opencl_builtin_pipe_first_arg) - << Call->getDirectCallee() << Call->getArg(0)->getSourceRange(); - return true; - } - - return false; -} - -// OpenCL v2.0 s6.13.9 - Address space qualifier functions. -// Performs semantic analysis for the to_global/local/private call. -// \param S Reference to the semantic analyzer. -// \param BuiltinID ID of the builtin function. -// \param Call A pointer to the builtin call. -// \return True if a semantic error has been found, false otherwise. -static bool OpenCLBuiltinToAddr(Sema &S, unsigned BuiltinID, CallExpr *Call) { - if (S.checkArgCount(Call, 1)) - return true; - - auto RT = Call->getArg(0)->getType(); - if (!RT->isPointerType() || RT->getPointeeType() - .getAddressSpace() == LangAS::opencl_constant) { - S.Diag(Call->getBeginLoc(), diag::err_opencl_builtin_to_addr_invalid_arg) - << Call->getArg(0) << Call->getDirectCallee() << Call->getSourceRange(); - return true; - } - - if (RT->getPointeeType().getAddressSpace() != LangAS::opencl_generic) { - S.Diag(Call->getArg(0)->getBeginLoc(), - diag::warn_opencl_generic_address_space_arg) - << Call->getDirectCallee()->getNameInfo().getAsString() - << Call->getArg(0)->getSourceRange(); - } - - RT = RT->getPointeeType(); - auto Qual = RT.getQualifiers(); - switch (BuiltinID) { - case Builtin::BIto_global: - Qual.setAddressSpace(LangAS::opencl_global); - break; - case Builtin::BIto_local: - Qual.setAddressSpace(LangAS::opencl_local); - break; - case Builtin::BIto_private: - Qual.setAddressSpace(LangAS::opencl_private); - break; - default: - llvm_unreachable("Invalid builtin function"); - } - Call->setType(S.Context.getPointerType(S.Context.getQualifiedType( - RT.getUnqualifiedType(), Qual))); - - return false; -} - namespace { enum PointerAuthOpKind { PAO_Strip, @@ -3078,59 +2558,59 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, case Builtin::BIwrite_pipe: // Since those two functions are declared with var args, we need a semantic // check for the argument. - if (BuiltinRWPipe(*this, TheCall)) + if (OpenCL().checkBuiltinRWPipe(TheCall)) return ExprError(); break; case Builtin::BIreserve_read_pipe: case Builtin::BIreserve_write_pipe: case Builtin::BIwork_group_reserve_read_pipe: case Builtin::BIwork_group_reserve_write_pipe: - if (BuiltinReserveRWPipe(*this, TheCall)) + if (OpenCL().checkBuiltinReserveRWPipe(TheCall)) return ExprError(); break; case Builtin::BIsub_group_reserve_read_pipe: case Builtin::BIsub_group_reserve_write_pipe: - if (checkOpenCLSubgroupExt(*this, TheCall) || - BuiltinReserveRWPipe(*this, TheCall)) + if (OpenCL().checkSubgroupExt(TheCall) || + OpenCL().checkBuiltinReserveRWPipe(TheCall)) return ExprError(); break; case Builtin::BIcommit_read_pipe: case Builtin::BIcommit_write_pipe: case Builtin::BIwork_group_commit_read_pipe: case Builtin::BIwork_group_commit_write_pipe: - if (BuiltinCommitRWPipe(*this, TheCall)) + if (OpenCL().checkBuiltinCommitRWPipe(TheCall)) return ExprError(); break; case Builtin::BIsub_group_commit_read_pipe: case Builtin::BIsub_group_commit_write_pipe: - if (checkOpenCLSubgroupExt(*this, TheCall) || - BuiltinCommitRWPipe(*this, TheCall)) + if (OpenCL().checkSubgroupExt(TheCall) || + OpenCL().checkBuiltinCommitRWPipe(TheCall)) return ExprError(); break; case Builtin::BIget_pipe_num_packets: case Builtin::BIget_pipe_max_packets: - if (BuiltinPipePackets(*this, TheCall)) + if (OpenCL().checkBuiltinPipePackets(TheCall)) return ExprError(); break; case Builtin::BIto_global: case Builtin::BIto_local: case Builtin::BIto_private: - if (OpenCLBuiltinToAddr(*this, BuiltinID, TheCall)) + if (OpenCL().checkBuiltinToAddr(BuiltinID, TheCall)) return ExprError(); break; // OpenCL v2.0, s6.13.17 - Enqueue kernel functions. case Builtin::BIenqueue_kernel: - if (OpenCLBuiltinEnqueueKernel(*this, TheCall)) + if (OpenCL().checkBuiltinEnqueueKernel(TheCall)) return ExprError(); break; case Builtin::BIget_kernel_work_group_size: case Builtin::BIget_kernel_preferred_work_group_size_multiple: - if (OpenCLBuiltinKernelWorkGroupSize(*this, TheCall)) + if (OpenCL().checkBuiltinKernelWorkGroupSize(TheCall)) return ExprError(); break; case Builtin::BIget_kernel_max_sub_group_size_for_ndrange: case Builtin::BIget_kernel_sub_group_count_for_ndrange: - if (OpenCLBuiltinNDRangeAndBlock(*this, TheCall)) + if (OpenCL().checkBuiltinNDRangeAndBlock(TheCall)) return ExprError(); break; case Builtin::BI__builtin_os_log_format: @@ -3425,7 +2905,7 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, } } - if (getLangOpts().HLSL && CheckHLSLBuiltinFunctionCall(BuiltinID, TheCall)) + if (getLangOpts().HLSL && HLSL().CheckBuiltinFunctionCall(BuiltinID, TheCall)) return ExprError(); // Since the target specific builtins for each arch overlap, only check those @@ -3473,234 +2953,6 @@ bool Sema::ValueIsRunOfOnes(CallExpr *TheCall, unsigned ArgNum) { << ArgNum << Arg->getSourceRange(); } -// Helper function for CheckHLSLBuiltinFunctionCall -bool CheckVectorElementCallArgs(Sema *S, CallExpr *TheCall) { - assert(TheCall->getNumArgs() > 1); - ExprResult A = TheCall->getArg(0); - - QualType ArgTyA = A.get()->getType(); - - auto *VecTyA = ArgTyA->getAs(); - SourceLocation BuiltinLoc = TheCall->getBeginLoc(); - - for (unsigned i = 1; i < TheCall->getNumArgs(); ++i) { - ExprResult B = TheCall->getArg(i); - QualType ArgTyB = B.get()->getType(); - auto *VecTyB = ArgTyB->getAs(); - if (VecTyA == nullptr && VecTyB == nullptr) - return false; - - if (VecTyA && VecTyB) { - bool retValue = false; - if (VecTyA->getElementType() != VecTyB->getElementType()) { - // Note: type promotion is intended to be handeled via the intrinsics - // and not the builtin itself. - S->Diag(TheCall->getBeginLoc(), - diag::err_vec_builtin_incompatible_vector) - << TheCall->getDirectCallee() << /*useAllTerminology*/ true - << SourceRange(A.get()->getBeginLoc(), B.get()->getEndLoc()); - retValue = true; - } - if (VecTyA->getNumElements() != VecTyB->getNumElements()) { - // You should only be hitting this case if you are calling the builtin - // directly. HLSL intrinsics should avoid this case via a - // HLSLVectorTruncation. - S->Diag(BuiltinLoc, diag::err_vec_builtin_incompatible_vector) - << TheCall->getDirectCallee() << /*useAllTerminology*/ true - << SourceRange(TheCall->getArg(0)->getBeginLoc(), - TheCall->getArg(1)->getEndLoc()); - retValue = true; - } - return retValue; - } - } - - // Note: if we get here one of the args is a scalar which - // requires a VectorSplat on Arg0 or Arg1 - S->Diag(BuiltinLoc, diag::err_vec_builtin_non_vector) - << TheCall->getDirectCallee() << /*useAllTerminology*/ true - << SourceRange(TheCall->getArg(0)->getBeginLoc(), - TheCall->getArg(1)->getEndLoc()); - return true; -} - -bool CheckArgsTypesAreCorrect( - Sema *S, CallExpr *TheCall, QualType ExpectedType, - llvm::function_ref Check) { - for (unsigned i = 0; i < TheCall->getNumArgs(); ++i) { - QualType PassedType = TheCall->getArg(i)->getType(); - if (Check(PassedType)) { - if (auto *VecTyA = PassedType->getAs()) - ExpectedType = S->Context.getVectorType( - ExpectedType, VecTyA->getNumElements(), VecTyA->getVectorKind()); - S->Diag(TheCall->getArg(0)->getBeginLoc(), - diag::err_typecheck_convert_incompatible) - << PassedType << ExpectedType << 1 << 0 << 0; - return true; - } - } - return false; -} - -bool CheckAllArgsHaveFloatRepresentation(Sema *S, CallExpr *TheCall) { - auto checkAllFloatTypes = [](clang::QualType PassedType) -> bool { - return !PassedType->hasFloatingRepresentation(); - }; - return CheckArgsTypesAreCorrect(S, TheCall, S->Context.FloatTy, - checkAllFloatTypes); -} - -bool CheckFloatOrHalfRepresentations(Sema *S, CallExpr *TheCall) { - auto checkFloatorHalf = [](clang::QualType PassedType) -> bool { - clang::QualType BaseType = - PassedType->isVectorType() - ? PassedType->getAs()->getElementType() - : PassedType; - return !BaseType->isHalfType() && !BaseType->isFloat32Type(); - }; - return CheckArgsTypesAreCorrect(S, TheCall, S->Context.FloatTy, - checkFloatorHalf); -} - -bool CheckNoDoubleVectors(Sema *S, CallExpr *TheCall) { - auto checkDoubleVector = [](clang::QualType PassedType) -> bool { - if (const auto *VecTy = PassedType->getAs()) - return VecTy->getElementType()->isDoubleType(); - return false; - }; - return CheckArgsTypesAreCorrect(S, TheCall, S->Context.FloatTy, - checkDoubleVector); -} - -bool CheckUnsignedIntRepresentation(Sema *S, CallExpr *TheCall) { - auto checkAllUnsignedTypes = [](clang::QualType PassedType) -> bool { - return !PassedType->hasUnsignedIntegerRepresentation(); - }; - return CheckArgsTypesAreCorrect(S, TheCall, S->Context.UnsignedIntTy, - checkAllUnsignedTypes); -} - -void SetElementTypeAsReturnType(Sema *S, CallExpr *TheCall, - QualType ReturnType) { - auto *VecTyA = TheCall->getArg(0)->getType()->getAs(); - if (VecTyA) - ReturnType = S->Context.getVectorType(ReturnType, VecTyA->getNumElements(), - VectorKind::Generic); - TheCall->setType(ReturnType); -} - -// Note: returning true in this case results in CheckBuiltinFunctionCall -// returning an ExprError -bool Sema::CheckHLSLBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { - switch (BuiltinID) { - case Builtin::BI__builtin_hlsl_elementwise_all: - case Builtin::BI__builtin_hlsl_elementwise_any: { - if (checkArgCount(TheCall, 1)) - return true; - break; - } - case Builtin::BI__builtin_hlsl_elementwise_clamp: { - if (checkArgCount(TheCall, 3)) - return true; - if (CheckVectorElementCallArgs(this, TheCall)) - return true; - if (BuiltinElementwiseTernaryMath( - TheCall, /*CheckForFloatArgs*/ - TheCall->getArg(0)->getType()->hasFloatingRepresentation())) - return true; - break; - } - case Builtin::BI__builtin_hlsl_dot: { - if (checkArgCount(TheCall, 2)) - return true; - if (CheckVectorElementCallArgs(this, TheCall)) - return true; - if (BuiltinVectorToScalarMath(TheCall)) - return true; - if (CheckNoDoubleVectors(this, TheCall)) - return true; - break; - } - case Builtin::BI__builtin_hlsl_elementwise_rcp: { - if (CheckAllArgsHaveFloatRepresentation(this, TheCall)) - return true; - if (PrepareBuiltinElementwiseMathOneArgCall(TheCall)) - return true; - break; - } - case Builtin::BI__builtin_hlsl_elementwise_rsqrt: - case Builtin::BI__builtin_hlsl_elementwise_frac: { - if (CheckFloatOrHalfRepresentations(this, TheCall)) - return true; - if (PrepareBuiltinElementwiseMathOneArgCall(TheCall)) - return true; - break; - } - case Builtin::BI__builtin_hlsl_elementwise_isinf: { - if (CheckFloatOrHalfRepresentations(this, TheCall)) - return true; - if (PrepareBuiltinElementwiseMathOneArgCall(TheCall)) - return true; - SetElementTypeAsReturnType(this, TheCall, this->Context.BoolTy); - break; - } - case Builtin::BI__builtin_hlsl_lerp: { - if (checkArgCount(TheCall, 3)) - return true; - if (CheckVectorElementCallArgs(this, TheCall)) - return true; - if (BuiltinElementwiseTernaryMath(TheCall)) - return true; - if (CheckFloatOrHalfRepresentations(this, TheCall)) - return true; - break; - } - case Builtin::BI__builtin_hlsl_mad: { - if (checkArgCount(TheCall, 3)) - return true; - if (CheckVectorElementCallArgs(this, TheCall)) - return true; - if (BuiltinElementwiseTernaryMath( - TheCall, /*CheckForFloatArgs*/ - TheCall->getArg(0)->getType()->hasFloatingRepresentation())) - return true; - break; - } - // Note these are llvm builtins that we want to catch invalid intrinsic - // generation. Normal handling of these builitns will occur elsewhere. - case Builtin::BI__builtin_elementwise_bitreverse: { - if (CheckUnsignedIntRepresentation(this, TheCall)) - return true; - break; - } - case Builtin::BI__builtin_elementwise_acos: - case Builtin::BI__builtin_elementwise_asin: - case Builtin::BI__builtin_elementwise_atan: - case Builtin::BI__builtin_elementwise_ceil: - case Builtin::BI__builtin_elementwise_cos: - case Builtin::BI__builtin_elementwise_cosh: - case Builtin::BI__builtin_elementwise_exp: - case Builtin::BI__builtin_elementwise_exp2: - case Builtin::BI__builtin_elementwise_floor: - case Builtin::BI__builtin_elementwise_log: - case Builtin::BI__builtin_elementwise_log2: - case Builtin::BI__builtin_elementwise_log10: - case Builtin::BI__builtin_elementwise_pow: - case Builtin::BI__builtin_elementwise_roundeven: - case Builtin::BI__builtin_elementwise_sin: - case Builtin::BI__builtin_elementwise_sinh: - case Builtin::BI__builtin_elementwise_sqrt: - case Builtin::BI__builtin_elementwise_tan: - case Builtin::BI__builtin_elementwise_tanh: - case Builtin::BI__builtin_elementwise_trunc: { - if (CheckFloatOrHalfRepresentations(this, TheCall)) - return true; - break; - } - } - return false; -} - /// Given a FunctionDecl's FormatAttr, attempts to populate the FomatStringInfo /// parameter with the FormatAttr's correct format_idx and firstDataArg. /// Returns true when the format fits the function and the FormatStringInfo has @@ -3772,58 +3024,6 @@ static void CheckNonNullArgument(Sema &S, << ArgExpr->getSourceRange()); } -bool Sema::GetFormatNSStringIdx(const FormatAttr *Format, unsigned &Idx) { - FormatStringInfo FSI; - if ((GetFormatStringType(Format) == FST_NSString) && - getFormatStringInfo(Format, false, true, &FSI)) { - Idx = FSI.FormatIdx; - return true; - } - return false; -} - -/// Diagnose use of %s directive in an NSString which is being passed -/// as formatting string to formatting method. -static void -DiagnoseCStringFormatDirectiveInCFAPI(Sema &S, - const NamedDecl *FDecl, - Expr **Args, - unsigned NumArgs) { - unsigned Idx = 0; - bool Format = false; - ObjCStringFormatFamily SFFamily = FDecl->getObjCFStringFormattingFamily(); - if (SFFamily == ObjCStringFormatFamily::SFF_CFString) { - Idx = 2; - Format = true; - } - else - for (const auto *I : FDecl->specific_attrs()) { - if (S.GetFormatNSStringIdx(I, Idx)) { - Format = true; - break; - } - } - if (!Format || NumArgs <= Idx) - return; - const Expr *FormatExpr = Args[Idx]; - if (const CStyleCastExpr *CSCE = dyn_cast(FormatExpr)) - FormatExpr = CSCE->getSubExpr(); - const StringLiteral *FormatString; - if (const ObjCStringLiteral *OSL = - dyn_cast(FormatExpr->IgnoreParenImpCasts())) - FormatString = OSL->getString(); - else - FormatString = dyn_cast(FormatExpr->IgnoreParenImpCasts()); - if (!FormatString) - return; - if (S.FormatStringHasSArg(FormatString)) { - S.Diag(FormatExpr->getExprLoc(), diag::warn_objc_cdirective_format_string) - << "%s" << 1 << 1; - S.Diag(FDecl->getLocation(), diag::note_entity_declared_at) - << FDecl->getDeclName(); - } -} - /// Determine whether the given type has a non-null nullability annotation. static bool isNonNullType(QualType type) { if (auto nullability = type->getNullability()) @@ -4240,7 +3440,7 @@ bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall, CheckInfNaNFunction(TheCall, FDecl); if (getLangOpts().ObjC) - DiagnoseCStringFormatDirectiveInCFAPI(*this, FDecl, Args, NumArgs); + ObjC().DiagnoseCStringFormatDirectiveInCFAPI(FDecl, Args, NumArgs); unsigned CMId = FDecl->getMemoryFunctionKind(); @@ -11306,26 +10506,6 @@ static void DiagnoseImpCast(Sema &S, Expr *E, QualType T, DiagnoseImpCast(S, E, E->getType(), T, CContext, diag, pruneControlFlow); } -static bool isObjCSignedCharBool(Sema &S, QualType Ty) { - return Ty->isSpecificBuiltinType(BuiltinType::SChar) && - S.getLangOpts().ObjC && S.ObjC().NSAPIObj->isObjCBOOLType(Ty); -} - -static void adornObjCBoolConversionDiagWithTernaryFixit( - Sema &S, Expr *SourceExpr, const Sema::SemaDiagnosticBuilder &Builder) { - Expr *Ignored = SourceExpr->IgnoreImplicit(); - if (const auto *OVE = dyn_cast(Ignored)) - Ignored = OVE->getSourceExpr(); - bool NeedsParens = isa(Ignored) || - isa(Ignored) || - isa(Ignored); - SourceLocation EndLoc = S.getLocForEndOfToken(SourceExpr->getEndLoc()); - if (NeedsParens) - Builder << FixItHint::CreateInsertion(SourceExpr->getBeginLoc(), "(") - << FixItHint::CreateInsertion(EndLoc, ")"); - Builder << FixItHint::CreateInsertion(EndLoc, " ? YES : NO"); -} - /// Diagnose an implicit cast from a floating point value to an integer value. static void DiagnoseFloatingImpCast(Sema &S, Expr *E, QualType T, SourceLocation CContext) { @@ -11345,11 +10525,10 @@ static void DiagnoseFloatingImpCast(Sema &S, Expr *E, QualType T, bool IsConstant = E->EvaluateAsFloat(Value, S.Context, Expr::SE_AllowSideEffects); if (!IsConstant) { - if (isObjCSignedCharBool(S, T)) { - return adornObjCBoolConversionDiagWithTernaryFixit( - S, E, - S.Diag(CContext, diag::warn_impcast_float_to_objc_signed_char_bool) - << E->getType()); + if (S.ObjC().isSignedCharBool(T)) { + return S.ObjC().adornBoolConversionDiagWithTernaryFixit( + E, S.Diag(CContext, diag::warn_impcast_float_to_objc_signed_char_bool) + << E->getType()); } return DiagnoseImpCast(S, E, T, CContext, @@ -11373,11 +10552,10 @@ static void DiagnoseFloatingImpCast(Sema &S, Expr *E, QualType T, precision = (precision * 59 + 195) / 196; Value.toString(PrettySourceValue, precision); - if (isObjCSignedCharBool(S, T) && IntegerValue != 0 && IntegerValue != 1) { - return adornObjCBoolConversionDiagWithTernaryFixit( - S, E, - S.Diag(CContext, diag::warn_impcast_constant_value_to_objc_bool) - << PrettySourceValue); + if (S.ObjC().isSignedCharBool(T) && IntegerValue != 0 && IntegerValue != 1) { + return S.ObjC().adornBoolConversionDiagWithTernaryFixit( + E, S.Diag(CContext, diag::warn_impcast_constant_value_to_objc_bool) + << PrettySourceValue); } if (Result == llvm::APFloat::opOK && isExact) { @@ -11578,102 +10756,6 @@ static void DiagnoseNullConversion(Sema &S, Expr *E, QualType T, S.getFixItZeroLiteralForType(T, Loc)); } -static void checkObjCArrayLiteral(Sema &S, QualType TargetType, - ObjCArrayLiteral *ArrayLiteral); - -static void -checkObjCDictionaryLiteral(Sema &S, QualType TargetType, - ObjCDictionaryLiteral *DictionaryLiteral); - -/// Check a single element within a collection literal against the -/// target element type. -static void checkObjCCollectionLiteralElement(Sema &S, - QualType TargetElementType, - Expr *Element, - unsigned ElementKind) { - // Skip a bitcast to 'id' or qualified 'id'. - if (auto ICE = dyn_cast(Element)) { - if (ICE->getCastKind() == CK_BitCast && - ICE->getSubExpr()->getType()->getAs()) - Element = ICE->getSubExpr(); - } - - QualType ElementType = Element->getType(); - ExprResult ElementResult(Element); - if (ElementType->getAs() && - S.CheckSingleAssignmentConstraints(TargetElementType, - ElementResult, - false, false) - != Sema::Compatible) { - S.Diag(Element->getBeginLoc(), diag::warn_objc_collection_literal_element) - << ElementType << ElementKind << TargetElementType - << Element->getSourceRange(); - } - - if (auto ArrayLiteral = dyn_cast(Element)) - checkObjCArrayLiteral(S, TargetElementType, ArrayLiteral); - else if (auto DictionaryLiteral = dyn_cast(Element)) - checkObjCDictionaryLiteral(S, TargetElementType, DictionaryLiteral); -} - -/// Check an Objective-C array literal being converted to the given -/// target type. -static void checkObjCArrayLiteral(Sema &S, QualType TargetType, - ObjCArrayLiteral *ArrayLiteral) { - if (!S.ObjC().NSArrayDecl) - return; - - const auto *TargetObjCPtr = TargetType->getAs(); - if (!TargetObjCPtr) - return; - - if (TargetObjCPtr->isUnspecialized() || - TargetObjCPtr->getInterfaceDecl()->getCanonicalDecl() != - S.ObjC().NSArrayDecl->getCanonicalDecl()) - return; - - auto TypeArgs = TargetObjCPtr->getTypeArgs(); - if (TypeArgs.size() != 1) - return; - - QualType TargetElementType = TypeArgs[0]; - for (unsigned I = 0, N = ArrayLiteral->getNumElements(); I != N; ++I) { - checkObjCCollectionLiteralElement(S, TargetElementType, - ArrayLiteral->getElement(I), - 0); - } -} - -/// Check an Objective-C dictionary literal being converted to the given -/// target type. -static void -checkObjCDictionaryLiteral(Sema &S, QualType TargetType, - ObjCDictionaryLiteral *DictionaryLiteral) { - if (!S.ObjC().NSDictionaryDecl) - return; - - const auto *TargetObjCPtr = TargetType->getAs(); - if (!TargetObjCPtr) - return; - - if (TargetObjCPtr->isUnspecialized() || - TargetObjCPtr->getInterfaceDecl()->getCanonicalDecl() != - S.ObjC().NSDictionaryDecl->getCanonicalDecl()) - return; - - auto TypeArgs = TargetObjCPtr->getTypeArgs(); - if (TypeArgs.size() != 2) - return; - - QualType TargetKeyType = TypeArgs[0]; - QualType TargetObjectType = TypeArgs[1]; - for (unsigned I = 0, N = DictionaryLiteral->getNumElements(); I != N; ++I) { - auto Element = DictionaryLiteral->getKeyValueElement(I); - checkObjCCollectionLiteralElement(S, TargetKeyType, Element.Key, 1); - checkObjCCollectionLiteralElement(S, TargetObjectType, Element.Value, 2); - } -} - // Helper function to filter out cases for constant width constant conversion. // Don't warn on char array initialization or for non-decimal values. static bool isSameWidthConstantConversion(Sema &S, Expr *E, QualType T, @@ -11749,14 +10831,12 @@ static void DiagnoseIntInBoolContext(Sema &S, Expr *E) { } } -static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, - SourceLocation CC, - bool *ICContext = nullptr, - bool IsListInit = false) { +void Sema::CheckImplicitConversion(Expr *E, QualType T, SourceLocation CC, + bool *ICContext, bool IsListInit) { if (E->isTypeDependent() || E->isValueDependent()) return; - const Type *Source = S.Context.getCanonicalType(E->getType()).getTypePtr(); - const Type *Target = S.Context.getCanonicalType(T).getTypePtr(); + const Type *Source = Context.getCanonicalType(E->getType()).getTypePtr(); + const Type *Target = Context.getCanonicalType(T).getTypePtr(); if (Source == Target) return; if (Target->isDependentType()) return; @@ -11769,7 +10849,7 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, return; if (Source->isAtomicType()) - S.Diag(E->getExprLoc(), diag::warn_atomic_implicit_seq_cst); + Diag(E->getExprLoc(), diag::warn_atomic_implicit_seq_cst); // Diagnose implicit casts to bool. if (Target->isSpecificBuiltinType(BuiltinType::Bool)) { @@ -11777,34 +10857,32 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, // Warn on string literal to bool. Checks for string literals in logical // and expressions, for instance, assert(0 && "error here"), are // prevented by a check in AnalyzeImplicitConversions(). - return DiagnoseImpCast(S, E, T, CC, + return DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_string_literal_to_bool); if (isa(E) || isa(E) || isa(E) || isa(E)) { // This covers the literal expressions that evaluate to Objective-C // objects. - return DiagnoseImpCast(S, E, T, CC, + return DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_objective_c_literal_to_bool); } if (Source->isPointerType() || Source->canDecayToPointerType()) { // Warn on pointer to bool conversion that is always true. - S.DiagnoseAlwaysNonNullPointer(E, Expr::NPCK_NotNull, /*IsEqual*/ false, - SourceRange(CC)); + DiagnoseAlwaysNonNullPointer(E, Expr::NPCK_NotNull, /*IsEqual*/ false, + SourceRange(CC)); } } // If the we're converting a constant to an ObjC BOOL on a platform where BOOL // is a typedef for signed char (macOS), then that constant value has to be 1 // or 0. - if (isObjCSignedCharBool(S, T) && Source->isIntegralType(S.Context)) { + if (ObjC().isSignedCharBool(T) && Source->isIntegralType(Context)) { Expr::EvalResult Result; - if (E->EvaluateAsInt(Result, S.getASTContext(), - Expr::SE_AllowSideEffects)) { + if (E->EvaluateAsInt(Result, getASTContext(), Expr::SE_AllowSideEffects)) { if (Result.Val.getInt() != 1 && Result.Val.getInt() != 0) { - adornObjCBoolConversionDiagWithTernaryFixit( - S, E, - S.Diag(CC, diag::warn_impcast_constant_value_to_objc_bool) - << toString(Result.Val.getInt(), 10)); + ObjC().adornBoolConversionDiagWithTernaryFixit( + E, Diag(CC, diag::warn_impcast_constant_value_to_objc_bool) + << toString(Result.Val.getInt(), 10)); } return; } @@ -11813,42 +10891,43 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, // Check implicit casts from Objective-C collection literals to specialized // collection types, e.g., NSArray *. if (auto *ArrayLiteral = dyn_cast(E)) - checkObjCArrayLiteral(S, QualType(Target, 0), ArrayLiteral); + ObjC().checkArrayLiteral(QualType(Target, 0), ArrayLiteral); else if (auto *DictionaryLiteral = dyn_cast(E)) - checkObjCDictionaryLiteral(S, QualType(Target, 0), DictionaryLiteral); + ObjC().checkDictionaryLiteral(QualType(Target, 0), DictionaryLiteral); // Strip vector types. if (isa(Source)) { if (Target->isSveVLSBuiltinType() && - (S.Context.areCompatibleSveTypes(QualType(Target, 0), - QualType(Source, 0)) || - S.Context.areLaxCompatibleSveTypes(QualType(Target, 0), - QualType(Source, 0)))) + (Context.areCompatibleSveTypes(QualType(Target, 0), + QualType(Source, 0)) || + Context.areLaxCompatibleSveTypes(QualType(Target, 0), + QualType(Source, 0)))) return; if (Target->isRVVVLSBuiltinType() && - (S.Context.areCompatibleRVVTypes(QualType(Target, 0), - QualType(Source, 0)) || - S.Context.areLaxCompatibleRVVTypes(QualType(Target, 0), - QualType(Source, 0)))) + (Context.areCompatibleRVVTypes(QualType(Target, 0), + QualType(Source, 0)) || + Context.areLaxCompatibleRVVTypes(QualType(Target, 0), + QualType(Source, 0)))) return; if (!isa(Target)) { - if (S.SourceMgr.isInSystemMacro(CC)) + if (SourceMgr.isInSystemMacro(CC)) return; - return DiagnoseImpCast(S, E, T, CC, diag::warn_impcast_vector_scalar); - } else if (S.getLangOpts().HLSL && + return DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_vector_scalar); + } else if (getLangOpts().HLSL && Target->castAs()->getNumElements() < Source->castAs()->getNumElements()) { // Diagnose vector truncation but don't return. We may also want to // diagnose an element conversion. - DiagnoseImpCast(S, E, T, CC, diag::warn_hlsl_impcast_vector_truncation); + DiagnoseImpCast(*this, E, T, CC, + diag::warn_hlsl_impcast_vector_truncation); } // If the vector cast is cast between two vectors of the same size, it is // a bitcast, not a conversion, except under HLSL where it is a conversion. - if (!S.getLangOpts().HLSL && - S.Context.getTypeSize(Source) == S.Context.getTypeSize(Target)) + if (!getLangOpts().HLSL && + Context.getTypeSize(Source) == Context.getTypeSize(Target)) return; Source = cast(Source)->getElementType().getTypePtr(); @@ -11860,11 +10939,11 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, // Strip complex types. if (isa(Source)) { if (!isa(Target)) { - if (S.SourceMgr.isInSystemMacro(CC) || Target->isBooleanType()) + if (SourceMgr.isInSystemMacro(CC) || Target->isBooleanType()) return; - return DiagnoseImpCast(S, E, T, CC, - S.getLangOpts().CPlusPlus + return DiagnoseImpCast(*this, E, T, CC, + getLangOpts().CPlusPlus ? diag::err_impcast_complex_scalar : diag::warn_impcast_complex_scalar); } @@ -11879,25 +10958,25 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, // Strip SVE vector types if (SourceBT && SourceBT->isSveVLSBuiltinType()) { // Need the original target type for vector type checks - const Type *OriginalTarget = S.Context.getCanonicalType(T).getTypePtr(); + const Type *OriginalTarget = Context.getCanonicalType(T).getTypePtr(); // Handle conversion from scalable to fixed when msve-vector-bits is // specified - if (S.Context.areCompatibleSveTypes(QualType(OriginalTarget, 0), - QualType(Source, 0)) || - S.Context.areLaxCompatibleSveTypes(QualType(OriginalTarget, 0), - QualType(Source, 0))) + if (Context.areCompatibleSveTypes(QualType(OriginalTarget, 0), + QualType(Source, 0)) || + Context.areLaxCompatibleSveTypes(QualType(OriginalTarget, 0), + QualType(Source, 0))) return; // If the vector cast is cast between two vectors of the same size, it is // a bitcast, not a conversion. - if (S.Context.getTypeSize(Source) == S.Context.getTypeSize(Target)) + if (Context.getTypeSize(Source) == Context.getTypeSize(Target)) return; - Source = SourceBT->getSveEltType(S.Context).getTypePtr(); + Source = SourceBT->getSveEltType(Context).getTypePtr(); } if (TargetBT && TargetBT->isSveVLSBuiltinType()) - Target = TargetBT->getSveEltType(S.Context).getTypePtr(); + Target = TargetBT->getSveEltType(Context).getTypePtr(); // If the source is floating point... if (SourceBT && SourceBT->isFloatingPoint()) { @@ -11905,41 +10984,42 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, if (TargetBT && TargetBT->isFloatingPoint()) { // ...then warn if we're dropping FP rank. - int Order = S.getASTContext().getFloatingTypeSemanticOrder( + int Order = getASTContext().getFloatingTypeSemanticOrder( QualType(SourceBT, 0), QualType(TargetBT, 0)); if (Order > 0) { // Don't warn about float constants that are precisely // representable in the target type. Expr::EvalResult result; - if (E->EvaluateAsRValue(result, S.Context)) { + if (E->EvaluateAsRValue(result, Context)) { // Value might be a float, a float vector, or a float complex. - if (IsSameFloatAfterCast(result.Val, - S.Context.getFloatTypeSemantics(QualType(TargetBT, 0)), - S.Context.getFloatTypeSemantics(QualType(SourceBT, 0)))) + if (IsSameFloatAfterCast( + result.Val, + Context.getFloatTypeSemantics(QualType(TargetBT, 0)), + Context.getFloatTypeSemantics(QualType(SourceBT, 0)))) return; } - if (S.SourceMgr.isInSystemMacro(CC)) + if (SourceMgr.isInSystemMacro(CC)) return; - DiagnoseImpCast(S, E, T, CC, diag::warn_impcast_float_precision); + DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_float_precision); } // ... or possibly if we're increasing rank, too else if (Order < 0) { - if (S.SourceMgr.isInSystemMacro(CC)) + if (SourceMgr.isInSystemMacro(CC)) return; - DiagnoseImpCast(S, E, T, CC, diag::warn_impcast_double_promotion); + DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_double_promotion); } return; } // If the target is integral, always warn. if (TargetBT && TargetBT->isInteger()) { - if (S.SourceMgr.isInSystemMacro(CC)) + if (SourceMgr.isInSystemMacro(CC)) return; - DiagnoseFloatingImpCast(S, E, T, CC); + DiagnoseFloatingImpCast(*this, E, T, CC); } // Detect the case where a call result is converted from floating-point to @@ -11961,7 +11041,7 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, if (isa(LastA) && InnerE->getType()->isBooleanType()) { // Warn on this floating-point to bool conversion - DiagnoseImpCast(S, E, T, CC, + DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_floating_point_to_bool); } } @@ -11973,38 +11053,37 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, if (Source->isFixedPointType()) { if (Target->isUnsaturatedFixedPointType()) { Expr::EvalResult Result; - if (E->EvaluateAsFixedPoint(Result, S.Context, Expr::SE_AllowSideEffects, - S.isConstantEvaluatedContext())) { + if (E->EvaluateAsFixedPoint(Result, Context, Expr::SE_AllowSideEffects, + isConstantEvaluatedContext())) { llvm::APFixedPoint Value = Result.Val.getFixedPoint(); - llvm::APFixedPoint MaxVal = S.Context.getFixedPointMax(T); - llvm::APFixedPoint MinVal = S.Context.getFixedPointMin(T); + llvm::APFixedPoint MaxVal = Context.getFixedPointMax(T); + llvm::APFixedPoint MinVal = Context.getFixedPointMin(T); if (Value > MaxVal || Value < MinVal) { - S.DiagRuntimeBehavior(E->getExprLoc(), E, - S.PDiag(diag::warn_impcast_fixed_point_range) - << Value.toString() << T - << E->getSourceRange() - << clang::SourceRange(CC)); + DiagRuntimeBehavior(E->getExprLoc(), E, + PDiag(diag::warn_impcast_fixed_point_range) + << Value.toString() << T + << E->getSourceRange() + << clang::SourceRange(CC)); return; } } } else if (Target->isIntegerType()) { Expr::EvalResult Result; - if (!S.isConstantEvaluatedContext() && - E->EvaluateAsFixedPoint(Result, S.Context, - Expr::SE_AllowSideEffects)) { + if (!isConstantEvaluatedContext() && + E->EvaluateAsFixedPoint(Result, Context, Expr::SE_AllowSideEffects)) { llvm::APFixedPoint FXResult = Result.Val.getFixedPoint(); bool Overflowed; llvm::APSInt IntResult = FXResult.convertToInt( - S.Context.getIntWidth(T), - Target->isSignedIntegerOrEnumerationType(), &Overflowed); + Context.getIntWidth(T), Target->isSignedIntegerOrEnumerationType(), + &Overflowed); if (Overflowed) { - S.DiagRuntimeBehavior(E->getExprLoc(), E, - S.PDiag(diag::warn_impcast_fixed_point_range) - << FXResult.toString() << T - << E->getSourceRange() - << clang::SourceRange(CC)); + DiagRuntimeBehavior(E->getExprLoc(), E, + PDiag(diag::warn_impcast_fixed_point_range) + << FXResult.toString() << T + << E->getSourceRange() + << clang::SourceRange(CC)); return; } } @@ -12012,20 +11091,20 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, } else if (Target->isUnsaturatedFixedPointType()) { if (Source->isIntegerType()) { Expr::EvalResult Result; - if (!S.isConstantEvaluatedContext() && - E->EvaluateAsInt(Result, S.Context, Expr::SE_AllowSideEffects)) { + if (!isConstantEvaluatedContext() && + E->EvaluateAsInt(Result, Context, Expr::SE_AllowSideEffects)) { llvm::APSInt Value = Result.Val.getInt(); bool Overflowed; llvm::APFixedPoint IntResult = llvm::APFixedPoint::getFromIntValue( - Value, S.Context.getFixedPointSemantics(T), &Overflowed); + Value, Context.getFixedPointSemantics(T), &Overflowed); if (Overflowed) { - S.DiagRuntimeBehavior(E->getExprLoc(), E, - S.PDiag(diag::warn_impcast_fixed_point_range) - << toString(Value, /*Radix=*/10) << T - << E->getSourceRange() - << clang::SourceRange(CC)); + DiagRuntimeBehavior(E->getExprLoc(), E, + PDiag(diag::warn_impcast_fixed_point_range) + << toString(Value, /*Radix=*/10) << T + << E->getSourceRange() + << clang::SourceRange(CC)); return; } } @@ -12039,25 +11118,25 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, TargetBT->isFloatingType() && !IsListInit) { // Determine the number of precision bits in the source integer type. IntRange SourceRange = - GetExprRange(S.Context, E, S.isConstantEvaluatedContext(), + GetExprRange(Context, E, isConstantEvaluatedContext(), /*Approximate=*/true); unsigned int SourcePrecision = SourceRange.Width; // Determine the number of precision bits in the // target floating point type. unsigned int TargetPrecision = llvm::APFloatBase::semanticsPrecision( - S.Context.getFloatTypeSemantics(QualType(TargetBT, 0))); + Context.getFloatTypeSemantics(QualType(TargetBT, 0))); if (SourcePrecision > 0 && TargetPrecision > 0 && SourcePrecision > TargetPrecision) { if (std::optional SourceInt = - E->getIntegerConstantExpr(S.Context)) { + E->getIntegerConstantExpr(Context)) { // If the source integer is a constant, convert it to the target // floating point type. Issue a warning if the value changes // during the whole conversion. llvm::APFloat TargetFloatValue( - S.Context.getFloatTypeSemantics(QualType(TargetBT, 0))); + Context.getFloatTypeSemantics(QualType(TargetBT, 0))); llvm::APFloat::opStatus ConversionStatus = TargetFloatValue.convertFromAPInt( *SourceInt, SourceBT->isSignedInteger(), @@ -12069,26 +11148,26 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, SmallString<32> PrettyTargetValue; TargetFloatValue.toString(PrettyTargetValue, TargetPrecision); - S.DiagRuntimeBehavior( + DiagRuntimeBehavior( E->getExprLoc(), E, - S.PDiag(diag::warn_impcast_integer_float_precision_constant) + PDiag(diag::warn_impcast_integer_float_precision_constant) << PrettySourceValue << PrettyTargetValue << E->getType() << T << E->getSourceRange() << clang::SourceRange(CC)); } } else { // Otherwise, the implicit conversion may lose precision. - DiagnoseImpCast(S, E, T, CC, + DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_integer_float_precision); } } } - DiagnoseNullConversion(S, E, T, CC); + DiagnoseNullConversion(*this, E, T, CC); - S.DiscardMisalignedMemberAddress(Target, E); + DiscardMisalignedMemberAddress(Target, E); if (Target->isBooleanType()) - DiagnoseIntInBoolContext(S, E); + DiagnoseIntInBoolContext(*this, E); if (!Source->isIntegerType() || !Target->isIntegerType()) return; @@ -12098,51 +11177,51 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, if (Target->isSpecificBuiltinType(BuiltinType::Bool)) return; - if (isObjCSignedCharBool(S, T) && !Source->isCharType() && + if (ObjC().isSignedCharBool(T) && !Source->isCharType() && !E->isKnownToHaveBooleanValue(/*Semantic=*/false)) { - return adornObjCBoolConversionDiagWithTernaryFixit( - S, E, - S.Diag(CC, diag::warn_impcast_int_to_objc_signed_char_bool) - << E->getType()); + return ObjC().adornBoolConversionDiagWithTernaryFixit( + E, Diag(CC, diag::warn_impcast_int_to_objc_signed_char_bool) + << E->getType()); } IntRange SourceTypeRange = - IntRange::forTargetOfCanonicalType(S.Context, Source); + IntRange::forTargetOfCanonicalType(Context, Source); IntRange LikelySourceRange = GetExprRange( - S.Context, E, S.isConstantEvaluatedContext(), /*Approximate=*/true); - IntRange TargetRange = IntRange::forTargetOfCanonicalType(S.Context, Target); + Context, E, isConstantEvaluatedContext(), /*Approximate=*/true); + IntRange TargetRange = IntRange::forTargetOfCanonicalType(Context, Target); if (LikelySourceRange.Width > TargetRange.Width) { // If the source is a constant, use a default-on diagnostic. // TODO: this should happen for bitfield stores, too. Expr::EvalResult Result; - if (E->EvaluateAsInt(Result, S.Context, Expr::SE_AllowSideEffects, - S.isConstantEvaluatedContext())) { + if (E->EvaluateAsInt(Result, Context, Expr::SE_AllowSideEffects, + isConstantEvaluatedContext())) { llvm::APSInt Value(32); Value = Result.Val.getInt(); - if (S.SourceMgr.isInSystemMacro(CC)) + if (SourceMgr.isInSystemMacro(CC)) return; std::string PrettySourceValue = toString(Value, 10); std::string PrettyTargetValue = PrettyPrintInRange(Value, TargetRange); - S.DiagRuntimeBehavior( - E->getExprLoc(), E, - S.PDiag(diag::warn_impcast_integer_precision_constant) - << PrettySourceValue << PrettyTargetValue << E->getType() << T - << E->getSourceRange() << SourceRange(CC)); + DiagRuntimeBehavior(E->getExprLoc(), E, + PDiag(diag::warn_impcast_integer_precision_constant) + << PrettySourceValue << PrettyTargetValue + << E->getType() << T << E->getSourceRange() + << SourceRange(CC)); return; } // People want to build with -Wshorten-64-to-32 and not -Wconversion. - if (S.SourceMgr.isInSystemMacro(CC)) + if (SourceMgr.isInSystemMacro(CC)) return; - if (TargetRange.Width == 32 && S.Context.getIntWidth(E->getType()) == 64) - return DiagnoseImpCast(S, E, T, CC, diag::warn_impcast_integer_64_32, + if (TargetRange.Width == 32 && Context.getIntWidth(E->getType()) == 64) + return DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_integer_64_32, /* pruneControlFlow */ true); - return DiagnoseImpCast(S, E, T, CC, diag::warn_impcast_integer_precision); + return DiagnoseImpCast(*this, E, T, CC, + diag::warn_impcast_integer_precision); } if (TargetRange.Width > SourceTypeRange.Width) { @@ -12150,10 +11229,10 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, if (UO->getOpcode() == UO_Minus) if (Source->isUnsignedIntegerType()) { if (Target->isUnsignedIntegerType()) - return DiagnoseImpCast(S, E, T, CC, + return DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_high_order_zero_bits); if (Target->isSignedIntegerType()) - return DiagnoseImpCast(S, E, T, CC, + return DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_nonnegative_result); } } @@ -12166,17 +11245,17 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, // cause a negative value to be stored. Expr::EvalResult Result; - if (E->EvaluateAsInt(Result, S.Context, Expr::SE_AllowSideEffects) && - !S.SourceMgr.isInSystemMacro(CC)) { + if (E->EvaluateAsInt(Result, Context, Expr::SE_AllowSideEffects) && + !SourceMgr.isInSystemMacro(CC)) { llvm::APSInt Value = Result.Val.getInt(); - if (isSameWidthConstantConversion(S, E, T, CC)) { + if (isSameWidthConstantConversion(*this, E, T, CC)) { std::string PrettySourceValue = toString(Value, 10); std::string PrettyTargetValue = PrettyPrintInRange(Value, TargetRange); - S.Diag(E->getExprLoc(), - S.PDiag(diag::warn_impcast_integer_precision_constant) - << PrettySourceValue << PrettyTargetValue << E->getType() - << T << E->getSourceRange() << SourceRange(CC)); + Diag(E->getExprLoc(), + PDiag(diag::warn_impcast_integer_precision_constant) + << PrettySourceValue << PrettyTargetValue << E->getType() << T + << E->getSourceRange() << SourceRange(CC)); return; } } @@ -12188,7 +11267,7 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, ((TargetRange.NonNegative && !LikelySourceRange.NonNegative) || (!TargetRange.NonNegative && LikelySourceRange.NonNegative && LikelySourceRange.Width == TargetRange.Width))) { - if (S.SourceMgr.isInSystemMacro(CC)) + if (SourceMgr.isInSystemMacro(CC)) return; if (SourceBT && SourceBT->isInteger() && TargetBT && @@ -12209,24 +11288,24 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, *ICContext = true; } - return DiagnoseImpCast(S, E, T, CC, DiagID); + return DiagnoseImpCast(*this, E, T, CC, DiagID); } // Diagnose conversions between different enumeration types. // In C, we pretend that the type of an EnumConstantDecl is its enumeration // type, to give us better diagnostics. - QualType SourceType = E->getEnumCoercedType(S.Context); - Source = S.Context.getCanonicalType(SourceType).getTypePtr(); + QualType SourceType = E->getEnumCoercedType(Context); + Source = Context.getCanonicalType(SourceType).getTypePtr(); if (const EnumType *SourceEnum = Source->getAs()) if (const EnumType *TargetEnum = Target->getAs()) if (SourceEnum->getDecl()->hasNameForLinkage() && TargetEnum->getDecl()->hasNameForLinkage() && SourceEnum != TargetEnum) { - if (S.SourceMgr.isInSystemMacro(CC)) + if (SourceMgr.isInSystemMacro(CC)) return; - return DiagnoseImpCast(S, E, SourceType, T, CC, + return DiagnoseImpCast(*this, E, SourceType, T, CC, diag::warn_impcast_different_enum_types); } } @@ -12246,7 +11325,7 @@ static void CheckConditionalOperand(Sema &S, Expr *E, QualType T, AnalyzeImplicitConversions(S, E, CC); if (E->getType() != T) - return CheckImplicitConversion(S, E, T, CC, &ICContext); + return S.CheckImplicitConversion(E, T, CC, &ICContext); } static void CheckConditionalOperator(Sema &S, AbstractConditionalOperator *E, @@ -12277,11 +11356,11 @@ static void CheckConditionalOperator(Sema &S, AbstractConditionalOperator *E, if (E->getType() == T) return; Suspicious = false; - CheckImplicitConversion(S, TrueExpr->IgnoreParenImpCasts(), - E->getType(), CC, &Suspicious); + S.CheckImplicitConversion(TrueExpr->IgnoreParenImpCasts(), E->getType(), CC, + &Suspicious); if (!Suspicious) - CheckImplicitConversion(S, E->getFalseExpr()->IgnoreParenImpCasts(), - E->getType(), CC, &Suspicious); + S.CheckImplicitConversion(E->getFalseExpr()->IgnoreParenImpCasts(), + E->getType(), CC, &Suspicious); } /// Check conversion of given expression to boolean. @@ -12294,7 +11373,7 @@ static void CheckBoolLikeConversion(Sema &S, Expr *E, SourceLocation CC) { return; if (E->IgnoreParenImpCasts()->getType()->isAtomicType()) return; - CheckImplicitConversion(S, E->IgnoreParenImpCasts(), S.Context.BoolTy, CC); + S.CheckImplicitConversion(E->IgnoreParenImpCasts(), S.Context.BoolTy, CC); } namespace { @@ -12384,7 +11463,7 @@ static void AnalyzeImplicitConversions( // The non-canonical typecheck is just an optimization; // CheckImplicitConversion will filter out dead implicit conversions. if (SourceExpr->getType() != T) - CheckImplicitConversion(S, SourceExpr, T, CC, nullptr, IsListInit); + S.CheckImplicitConversion(SourceExpr, T, CC, nullptr, IsListInit); // Now continue drilling into this expression. @@ -12484,21 +11563,6 @@ static void AnalyzeImplicitConversions(Sema &S, Expr *OrigE, SourceLocation CC, AnalyzeImplicitConversions(S, WorkList.pop_back_val(), WorkList); } -/// Diagnose integer type and any valid implicit conversion to it. -static bool checkOpenCLEnqueueIntType(Sema &S, Expr *E, const QualType &IntT) { - // Taking into account implicit conversions, - // allow any integer. - if (!E->getType()->isIntegerType()) { - S.Diag(E->getBeginLoc(), - diag::err_opencl_enqueue_kernel_invalid_local_size_type); - return true; - } - // Potentially emit standard warnings for implicit conversions if enabled - // using -Wconversion. - CheckImplicitConversion(S, E, IntT, E->getBeginLoc()); - return false; -} - // Helper function for Sema::DiagnoseAlwaysNonNullPointer. // Returns true when emitting a warning about taking the address of a reference. static bool CheckForReference(Sema &SemaRef, const Expr *E, diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp index 9c423529c80e77..7ccecf055feedd 100644 --- a/clang/lib/Sema/SemaExprObjC.cpp +++ b/clang/lib/Sema/SemaExprObjC.cpp @@ -2564,7 +2564,7 @@ DiagnoseCStringFormatDirectiveInObjCAPI(Sema &S, } else if (Method) { for (const auto *I : Method->specific_attrs()) { - if (S.GetFormatNSStringIdx(I, Idx)) { + if (S.ObjC().GetFormatNSStringIdx(I, Idx)) { Format = true; break; } diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index cc9c2598581488..c39a24043d6645 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -852,3 +852,231 @@ void SemaHLSL::DiagnoseAvailabilityViolations(TranslationUnitDecl *TU) { DiagnoseHLSLAvailability(SemaRef).RunOnTranslationUnit(TU); } + +// Helper function for CheckHLSLBuiltinFunctionCall +bool CheckVectorElementCallArgs(Sema *S, CallExpr *TheCall) { + assert(TheCall->getNumArgs() > 1); + ExprResult A = TheCall->getArg(0); + + QualType ArgTyA = A.get()->getType(); + + auto *VecTyA = ArgTyA->getAs(); + SourceLocation BuiltinLoc = TheCall->getBeginLoc(); + + for (unsigned i = 1; i < TheCall->getNumArgs(); ++i) { + ExprResult B = TheCall->getArg(i); + QualType ArgTyB = B.get()->getType(); + auto *VecTyB = ArgTyB->getAs(); + if (VecTyA == nullptr && VecTyB == nullptr) + return false; + + if (VecTyA && VecTyB) { + bool retValue = false; + if (VecTyA->getElementType() != VecTyB->getElementType()) { + // Note: type promotion is intended to be handeled via the intrinsics + // and not the builtin itself. + S->Diag(TheCall->getBeginLoc(), + diag::err_vec_builtin_incompatible_vector) + << TheCall->getDirectCallee() << /*useAllTerminology*/ true + << SourceRange(A.get()->getBeginLoc(), B.get()->getEndLoc()); + retValue = true; + } + if (VecTyA->getNumElements() != VecTyB->getNumElements()) { + // You should only be hitting this case if you are calling the builtin + // directly. HLSL intrinsics should avoid this case via a + // HLSLVectorTruncation. + S->Diag(BuiltinLoc, diag::err_vec_builtin_incompatible_vector) + << TheCall->getDirectCallee() << /*useAllTerminology*/ true + << SourceRange(TheCall->getArg(0)->getBeginLoc(), + TheCall->getArg(1)->getEndLoc()); + retValue = true; + } + return retValue; + } + } + + // Note: if we get here one of the args is a scalar which + // requires a VectorSplat on Arg0 or Arg1 + S->Diag(BuiltinLoc, diag::err_vec_builtin_non_vector) + << TheCall->getDirectCallee() << /*useAllTerminology*/ true + << SourceRange(TheCall->getArg(0)->getBeginLoc(), + TheCall->getArg(1)->getEndLoc()); + return true; +} + +bool CheckArgsTypesAreCorrect( + Sema *S, CallExpr *TheCall, QualType ExpectedType, + llvm::function_ref Check) { + for (unsigned i = 0; i < TheCall->getNumArgs(); ++i) { + QualType PassedType = TheCall->getArg(i)->getType(); + if (Check(PassedType)) { + if (auto *VecTyA = PassedType->getAs()) + ExpectedType = S->Context.getVectorType( + ExpectedType, VecTyA->getNumElements(), VecTyA->getVectorKind()); + S->Diag(TheCall->getArg(0)->getBeginLoc(), + diag::err_typecheck_convert_incompatible) + << PassedType << ExpectedType << 1 << 0 << 0; + return true; + } + } + return false; +} + +bool CheckAllArgsHaveFloatRepresentation(Sema *S, CallExpr *TheCall) { + auto checkAllFloatTypes = [](clang::QualType PassedType) -> bool { + return !PassedType->hasFloatingRepresentation(); + }; + return CheckArgsTypesAreCorrect(S, TheCall, S->Context.FloatTy, + checkAllFloatTypes); +} + +bool CheckFloatOrHalfRepresentations(Sema *S, CallExpr *TheCall) { + auto checkFloatorHalf = [](clang::QualType PassedType) -> bool { + clang::QualType BaseType = + PassedType->isVectorType() + ? PassedType->getAs()->getElementType() + : PassedType; + return !BaseType->isHalfType() && !BaseType->isFloat32Type(); + }; + return CheckArgsTypesAreCorrect(S, TheCall, S->Context.FloatTy, + checkFloatorHalf); +} + +bool CheckNoDoubleVectors(Sema *S, CallExpr *TheCall) { + auto checkDoubleVector = [](clang::QualType PassedType) -> bool { + if (const auto *VecTy = PassedType->getAs()) + return VecTy->getElementType()->isDoubleType(); + return false; + }; + return CheckArgsTypesAreCorrect(S, TheCall, S->Context.FloatTy, + checkDoubleVector); +} + +bool CheckUnsignedIntRepresentation(Sema *S, CallExpr *TheCall) { + auto checkAllUnsignedTypes = [](clang::QualType PassedType) -> bool { + return !PassedType->hasUnsignedIntegerRepresentation(); + }; + return CheckArgsTypesAreCorrect(S, TheCall, S->Context.UnsignedIntTy, + checkAllUnsignedTypes); +} + +void SetElementTypeAsReturnType(Sema *S, CallExpr *TheCall, + QualType ReturnType) { + auto *VecTyA = TheCall->getArg(0)->getType()->getAs(); + if (VecTyA) + ReturnType = S->Context.getVectorType(ReturnType, VecTyA->getNumElements(), + VectorKind::Generic); + TheCall->setType(ReturnType); +} + +// Note: returning true in this case results in CheckBuiltinFunctionCall +// returning an ExprError +bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { + switch (BuiltinID) { + case Builtin::BI__builtin_hlsl_elementwise_all: + case Builtin::BI__builtin_hlsl_elementwise_any: { + if (SemaRef.checkArgCount(TheCall, 1)) + return true; + break; + } + case Builtin::BI__builtin_hlsl_elementwise_clamp: { + if (SemaRef.checkArgCount(TheCall, 3)) + return true; + if (CheckVectorElementCallArgs(&SemaRef, TheCall)) + return true; + if (SemaRef.BuiltinElementwiseTernaryMath( + TheCall, /*CheckForFloatArgs*/ + TheCall->getArg(0)->getType()->hasFloatingRepresentation())) + return true; + break; + } + case Builtin::BI__builtin_hlsl_dot: { + if (SemaRef.checkArgCount(TheCall, 2)) + return true; + if (CheckVectorElementCallArgs(&SemaRef, TheCall)) + return true; + if (SemaRef.BuiltinVectorToScalarMath(TheCall)) + return true; + if (CheckNoDoubleVectors(&SemaRef, TheCall)) + return true; + break; + } + case Builtin::BI__builtin_hlsl_elementwise_rcp: { + if (CheckAllArgsHaveFloatRepresentation(&SemaRef, TheCall)) + return true; + if (SemaRef.PrepareBuiltinElementwiseMathOneArgCall(TheCall)) + return true; + break; + } + case Builtin::BI__builtin_hlsl_elementwise_rsqrt: + case Builtin::BI__builtin_hlsl_elementwise_frac: { + if (CheckFloatOrHalfRepresentations(&SemaRef, TheCall)) + return true; + if (SemaRef.PrepareBuiltinElementwiseMathOneArgCall(TheCall)) + return true; + break; + } + case Builtin::BI__builtin_hlsl_elementwise_isinf: { + if (CheckFloatOrHalfRepresentations(&SemaRef, TheCall)) + return true; + if (SemaRef.PrepareBuiltinElementwiseMathOneArgCall(TheCall)) + return true; + SetElementTypeAsReturnType(&SemaRef, TheCall, getASTContext().BoolTy); + break; + } + case Builtin::BI__builtin_hlsl_lerp: { + if (SemaRef.checkArgCount(TheCall, 3)) + return true; + if (CheckVectorElementCallArgs(&SemaRef, TheCall)) + return true; + if (SemaRef.BuiltinElementwiseTernaryMath(TheCall)) + return true; + if (CheckFloatOrHalfRepresentations(&SemaRef, TheCall)) + return true; + break; + } + case Builtin::BI__builtin_hlsl_mad: { + if (SemaRef.checkArgCount(TheCall, 3)) + return true; + if (CheckVectorElementCallArgs(&SemaRef, TheCall)) + return true; + if (SemaRef.BuiltinElementwiseTernaryMath( + TheCall, /*CheckForFloatArgs*/ + TheCall->getArg(0)->getType()->hasFloatingRepresentation())) + return true; + break; + } + // Note these are llvm builtins that we want to catch invalid intrinsic + // generation. Normal handling of these builitns will occur elsewhere. + case Builtin::BI__builtin_elementwise_bitreverse: { + if (CheckUnsignedIntRepresentation(&SemaRef, TheCall)) + return true; + break; + } + case Builtin::BI__builtin_elementwise_acos: + case Builtin::BI__builtin_elementwise_asin: + case Builtin::BI__builtin_elementwise_atan: + case Builtin::BI__builtin_elementwise_ceil: + case Builtin::BI__builtin_elementwise_cos: + case Builtin::BI__builtin_elementwise_cosh: + case Builtin::BI__builtin_elementwise_exp: + case Builtin::BI__builtin_elementwise_exp2: + case Builtin::BI__builtin_elementwise_floor: + case Builtin::BI__builtin_elementwise_log: + case Builtin::BI__builtin_elementwise_log2: + case Builtin::BI__builtin_elementwise_log10: + case Builtin::BI__builtin_elementwise_pow: + case Builtin::BI__builtin_elementwise_roundeven: + case Builtin::BI__builtin_elementwise_sin: + case Builtin::BI__builtin_elementwise_sinh: + case Builtin::BI__builtin_elementwise_sqrt: + case Builtin::BI__builtin_elementwise_tan: + case Builtin::BI__builtin_elementwise_tanh: + case Builtin::BI__builtin_elementwise_trunc: { + if (CheckFloatOrHalfRepresentations(&SemaRef, TheCall)) + return true; + break; + } + } + return false; +} diff --git a/clang/lib/Sema/SemaObjC.cpp b/clang/lib/Sema/SemaObjC.cpp index d396258cfc7d18..75233689769c5b 100644 --- a/clang/lib/Sema/SemaObjC.cpp +++ b/clang/lib/Sema/SemaObjC.cpp @@ -2255,4 +2255,154 @@ void SemaObjC::handleExternallyRetainedAttr(Decl *D, const ParsedAttr &AL) { handleSimpleAttribute(*this, D, AL); } +bool SemaObjC::GetFormatNSStringIdx(const FormatAttr *Format, unsigned &Idx) { + Sema::FormatStringInfo FSI; + if ((SemaRef.GetFormatStringType(Format) == Sema::FST_NSString) && + SemaRef.getFormatStringInfo(Format, false, true, &FSI)) { + Idx = FSI.FormatIdx; + return true; + } + return false; +} + +/// Diagnose use of %s directive in an NSString which is being passed +/// as formatting string to formatting method. +void SemaObjC::DiagnoseCStringFormatDirectiveInCFAPI(const NamedDecl *FDecl, + Expr **Args, + unsigned NumArgs) { + unsigned Idx = 0; + bool Format = false; + ObjCStringFormatFamily SFFamily = FDecl->getObjCFStringFormattingFamily(); + if (SFFamily == ObjCStringFormatFamily::SFF_CFString) { + Idx = 2; + Format = true; + } else + for (const auto *I : FDecl->specific_attrs()) { + if (GetFormatNSStringIdx(I, Idx)) { + Format = true; + break; + } + } + if (!Format || NumArgs <= Idx) + return; + const Expr *FormatExpr = Args[Idx]; + if (const CStyleCastExpr *CSCE = dyn_cast(FormatExpr)) + FormatExpr = CSCE->getSubExpr(); + const StringLiteral *FormatString; + if (const ObjCStringLiteral *OSL = + dyn_cast(FormatExpr->IgnoreParenImpCasts())) + FormatString = OSL->getString(); + else + FormatString = dyn_cast(FormatExpr->IgnoreParenImpCasts()); + if (!FormatString) + return; + if (SemaRef.FormatStringHasSArg(FormatString)) { + Diag(FormatExpr->getExprLoc(), diag::warn_objc_cdirective_format_string) + << "%s" << 1 << 1; + Diag(FDecl->getLocation(), diag::note_entity_declared_at) + << FDecl->getDeclName(); + } +} + +bool SemaObjC::isSignedCharBool(QualType Ty) { + return Ty->isSpecificBuiltinType(BuiltinType::SChar) && getLangOpts().ObjC && + NSAPIObj->isObjCBOOLType(Ty); +} + +void SemaObjC::adornBoolConversionDiagWithTernaryFixit( + Expr *SourceExpr, const Sema::SemaDiagnosticBuilder &Builder) { + Expr *Ignored = SourceExpr->IgnoreImplicit(); + if (const auto *OVE = dyn_cast(Ignored)) + Ignored = OVE->getSourceExpr(); + bool NeedsParens = isa(Ignored) || + isa(Ignored) || + isa(Ignored); + SourceLocation EndLoc = SemaRef.getLocForEndOfToken(SourceExpr->getEndLoc()); + if (NeedsParens) + Builder << FixItHint::CreateInsertion(SourceExpr->getBeginLoc(), "(") + << FixItHint::CreateInsertion(EndLoc, ")"); + Builder << FixItHint::CreateInsertion(EndLoc, " ? YES : NO"); +} + +/// Check a single element within a collection literal against the +/// target element type. +static void checkCollectionLiteralElement(Sema &S, QualType TargetElementType, + Expr *Element, unsigned ElementKind) { + // Skip a bitcast to 'id' or qualified 'id'. + if (auto ICE = dyn_cast(Element)) { + if (ICE->getCastKind() == CK_BitCast && + ICE->getSubExpr()->getType()->getAs()) + Element = ICE->getSubExpr(); + } + + QualType ElementType = Element->getType(); + ExprResult ElementResult(Element); + if (ElementType->getAs() && + S.CheckSingleAssignmentConstraints(TargetElementType, ElementResult, + false, false) != Sema::Compatible) { + S.Diag(Element->getBeginLoc(), diag::warn_objc_collection_literal_element) + << ElementType << ElementKind << TargetElementType + << Element->getSourceRange(); + } + + if (auto ArrayLiteral = dyn_cast(Element)) + S.ObjC().checkArrayLiteral(TargetElementType, ArrayLiteral); + else if (auto DictionaryLiteral = dyn_cast(Element)) + S.ObjC().checkDictionaryLiteral(TargetElementType, DictionaryLiteral); +} + +/// Check an Objective-C array literal being converted to the given +/// target type. +void SemaObjC::checkArrayLiteral(QualType TargetType, + ObjCArrayLiteral *ArrayLiteral) { + if (!NSArrayDecl) + return; + + const auto *TargetObjCPtr = TargetType->getAs(); + if (!TargetObjCPtr) + return; + + if (TargetObjCPtr->isUnspecialized() || + TargetObjCPtr->getInterfaceDecl()->getCanonicalDecl() != + NSArrayDecl->getCanonicalDecl()) + return; + + auto TypeArgs = TargetObjCPtr->getTypeArgs(); + if (TypeArgs.size() != 1) + return; + + QualType TargetElementType = TypeArgs[0]; + for (unsigned I = 0, N = ArrayLiteral->getNumElements(); I != N; ++I) { + checkCollectionLiteralElement(SemaRef, TargetElementType, + ArrayLiteral->getElement(I), 0); + } +} + +void SemaObjC::checkDictionaryLiteral( + QualType TargetType, ObjCDictionaryLiteral *DictionaryLiteral) { + if (!NSDictionaryDecl) + return; + + const auto *TargetObjCPtr = TargetType->getAs(); + if (!TargetObjCPtr) + return; + + if (TargetObjCPtr->isUnspecialized() || + TargetObjCPtr->getInterfaceDecl()->getCanonicalDecl() != + NSDictionaryDecl->getCanonicalDecl()) + return; + + auto TypeArgs = TargetObjCPtr->getTypeArgs(); + if (TypeArgs.size() != 2) + return; + + QualType TargetKeyType = TypeArgs[0]; + QualType TargetObjectType = TypeArgs[1]; + for (unsigned I = 0, N = DictionaryLiteral->getNumElements(); I != N; ++I) { + auto Element = DictionaryLiteral->getKeyValueElement(I); + checkCollectionLiteralElement(SemaRef, TargetKeyType, Element.Key, 1); + checkCollectionLiteralElement(SemaRef, TargetObjectType, Element.Value, 2); + } +} + } // namespace clang diff --git a/clang/lib/Sema/SemaOpenCL.cpp b/clang/lib/Sema/SemaOpenCL.cpp index b3b495a15e02c3..9f746fffd34d0a 100644 --- a/clang/lib/Sema/SemaOpenCL.cpp +++ b/clang/lib/Sema/SemaOpenCL.cpp @@ -96,4 +96,483 @@ void SemaOpenCL::handleSubGroupSize(Decl *D, const ParsedAttr &AL) { OpenCLIntelReqdSubGroupSizeAttr(getASTContext(), AL, SGSize)); } +static inline bool isBlockPointer(Expr *Arg) { + return Arg->getType()->isBlockPointerType(); +} + +/// OpenCL C v2.0, s6.13.17.2 - Checks that the block parameters are all local +/// void*, which is a requirement of device side enqueue. +static bool checkBlockArgs(Sema &S, Expr *BlockArg) { + const BlockPointerType *BPT = + cast(BlockArg->getType().getCanonicalType()); + ArrayRef Params = + BPT->getPointeeType()->castAs()->getParamTypes(); + unsigned ArgCounter = 0; + bool IllegalParams = false; + // Iterate through the block parameters until either one is found that is not + // a local void*, or the block is valid. + for (ArrayRef::iterator I = Params.begin(), E = Params.end(); + I != E; ++I, ++ArgCounter) { + if (!(*I)->isPointerType() || !(*I)->getPointeeType()->isVoidType() || + (*I)->getPointeeType().getQualifiers().getAddressSpace() != + LangAS::opencl_local) { + // Get the location of the error. If a block literal has been passed + // (BlockExpr) then we can point straight to the offending argument, + // else we just point to the variable reference. + SourceLocation ErrorLoc; + if (isa(BlockArg)) { + BlockDecl *BD = cast(BlockArg)->getBlockDecl(); + ErrorLoc = BD->getParamDecl(ArgCounter)->getBeginLoc(); + } else if (isa(BlockArg)) { + ErrorLoc = cast(BlockArg)->getBeginLoc(); + } + S.Diag(ErrorLoc, + diag::err_opencl_enqueue_kernel_blocks_non_local_void_args); + IllegalParams = true; + } + } + + return IllegalParams; +} + +bool SemaOpenCL::checkSubgroupExt(CallExpr *Call) { + // OpenCL device can support extension but not the feature as extension + // requires subgroup independent forward progress, but subgroup independent + // forward progress is optional in OpenCL C 3.0 __opencl_c_subgroups feature. + if (!SemaRef.getOpenCLOptions().isSupported("cl_khr_subgroups", + getLangOpts()) && + !SemaRef.getOpenCLOptions().isSupported("__opencl_c_subgroups", + getLangOpts())) { + Diag(Call->getBeginLoc(), diag::err_opencl_requires_extension) + << 1 << Call->getDirectCallee() + << "cl_khr_subgroups or __opencl_c_subgroups"; + return true; + } + return false; +} + +bool SemaOpenCL::checkBuiltinNDRangeAndBlock(CallExpr *TheCall) { + if (SemaRef.checkArgCount(TheCall, 2)) + return true; + + if (checkSubgroupExt(TheCall)) + return true; + + // First argument is an ndrange_t type. + Expr *NDRangeArg = TheCall->getArg(0); + if (NDRangeArg->getType().getUnqualifiedType().getAsString() != "ndrange_t") { + Diag(NDRangeArg->getBeginLoc(), diag::err_opencl_builtin_expected_type) + << TheCall->getDirectCallee() << "'ndrange_t'"; + return true; + } + + Expr *BlockArg = TheCall->getArg(1); + if (!isBlockPointer(BlockArg)) { + Diag(BlockArg->getBeginLoc(), diag::err_opencl_builtin_expected_type) + << TheCall->getDirectCallee() << "block"; + return true; + } + return checkBlockArgs(SemaRef, BlockArg); +} + +bool SemaOpenCL::checkBuiltinKernelWorkGroupSize(CallExpr *TheCall) { + if (SemaRef.checkArgCount(TheCall, 1)) + return true; + + Expr *BlockArg = TheCall->getArg(0); + if (!isBlockPointer(BlockArg)) { + Diag(BlockArg->getBeginLoc(), diag::err_opencl_builtin_expected_type) + << TheCall->getDirectCallee() << "block"; + return true; + } + return checkBlockArgs(SemaRef, BlockArg); +} + +/// Diagnose integer type and any valid implicit conversion to it. +static bool checkOpenCLEnqueueIntType(Sema &S, Expr *E, const QualType &IntT) { + // Taking into account implicit conversions, + // allow any integer. + if (!E->getType()->isIntegerType()) { + S.Diag(E->getBeginLoc(), + diag::err_opencl_enqueue_kernel_invalid_local_size_type); + return true; + } + // Potentially emit standard warnings for implicit conversions if enabled + // using -Wconversion. + S.CheckImplicitConversion(E, IntT, E->getBeginLoc()); + return false; +} + +static bool checkOpenCLEnqueueLocalSizeArgs(Sema &S, CallExpr *TheCall, + unsigned Start, unsigned End) { + bool IllegalParams = false; + for (unsigned I = Start; I <= End; ++I) + IllegalParams |= checkOpenCLEnqueueIntType(S, TheCall->getArg(I), + S.Context.getSizeType()); + return IllegalParams; +} + +/// OpenCL v2.0, s6.13.17.1 - Check that sizes are provided for all +/// 'local void*' parameter of passed block. +static bool checkOpenCLEnqueueVariadicArgs(Sema &S, CallExpr *TheCall, + Expr *BlockArg, + unsigned NumNonVarArgs) { + const BlockPointerType *BPT = + cast(BlockArg->getType().getCanonicalType()); + unsigned NumBlockParams = + BPT->getPointeeType()->castAs()->getNumParams(); + unsigned TotalNumArgs = TheCall->getNumArgs(); + + // For each argument passed to the block, a corresponding uint needs to + // be passed to describe the size of the local memory. + if (TotalNumArgs != NumBlockParams + NumNonVarArgs) { + S.Diag(TheCall->getBeginLoc(), + diag::err_opencl_enqueue_kernel_local_size_args); + return true; + } + + // Check that the sizes of the local memory are specified by integers. + return checkOpenCLEnqueueLocalSizeArgs(S, TheCall, NumNonVarArgs, + TotalNumArgs - 1); +} + +bool SemaOpenCL::checkBuiltinEnqueueKernel(CallExpr *TheCall) { + ASTContext &Context = getASTContext(); + unsigned NumArgs = TheCall->getNumArgs(); + + if (NumArgs < 4) { + Diag(TheCall->getBeginLoc(), diag::err_typecheck_call_too_few_args_at_least) + << 0 << 4 << NumArgs << /*is non object*/ 0; + return true; + } + + Expr *Arg0 = TheCall->getArg(0); + Expr *Arg1 = TheCall->getArg(1); + Expr *Arg2 = TheCall->getArg(2); + Expr *Arg3 = TheCall->getArg(3); + + // First argument always needs to be a queue_t type. + if (!Arg0->getType()->isQueueT()) { + Diag(TheCall->getArg(0)->getBeginLoc(), + diag::err_opencl_builtin_expected_type) + << TheCall->getDirectCallee() << getASTContext().OCLQueueTy; + return true; + } + + // Second argument always needs to be a kernel_enqueue_flags_t enum value. + if (!Arg1->getType()->isIntegerType()) { + Diag(TheCall->getArg(1)->getBeginLoc(), + diag::err_opencl_builtin_expected_type) + << TheCall->getDirectCallee() << "'kernel_enqueue_flags_t' (i.e. uint)"; + return true; + } + + // Third argument is always an ndrange_t type. + if (Arg2->getType().getUnqualifiedType().getAsString() != "ndrange_t") { + Diag(TheCall->getArg(2)->getBeginLoc(), + diag::err_opencl_builtin_expected_type) + << TheCall->getDirectCallee() << "'ndrange_t'"; + return true; + } + + // With four arguments, there is only one form that the function could be + // called in: no events and no variable arguments. + if (NumArgs == 4) { + // check that the last argument is the right block type. + if (!isBlockPointer(Arg3)) { + Diag(Arg3->getBeginLoc(), diag::err_opencl_builtin_expected_type) + << TheCall->getDirectCallee() << "block"; + return true; + } + // we have a block type, check the prototype + const BlockPointerType *BPT = + cast(Arg3->getType().getCanonicalType()); + if (BPT->getPointeeType()->castAs()->getNumParams() > + 0) { + Diag(Arg3->getBeginLoc(), diag::err_opencl_enqueue_kernel_blocks_no_args); + return true; + } + return false; + } + // we can have block + varargs. + if (isBlockPointer(Arg3)) + return (checkBlockArgs(SemaRef, Arg3) || + checkOpenCLEnqueueVariadicArgs(SemaRef, TheCall, Arg3, 4)); + // last two cases with either exactly 7 args or 7 args and varargs. + if (NumArgs >= 7) { + // check common block argument. + Expr *Arg6 = TheCall->getArg(6); + if (!isBlockPointer(Arg6)) { + Diag(Arg6->getBeginLoc(), diag::err_opencl_builtin_expected_type) + << TheCall->getDirectCallee() << "block"; + return true; + } + if (checkBlockArgs(SemaRef, Arg6)) + return true; + + // Forth argument has to be any integer type. + if (!Arg3->getType()->isIntegerType()) { + Diag(TheCall->getArg(3)->getBeginLoc(), + diag::err_opencl_builtin_expected_type) + << TheCall->getDirectCallee() << "integer"; + return true; + } + // check remaining common arguments. + Expr *Arg4 = TheCall->getArg(4); + Expr *Arg5 = TheCall->getArg(5); + + // Fifth argument is always passed as a pointer to clk_event_t. + if (!Arg4->isNullPointerConstant(Context, + Expr::NPC_ValueDependentIsNotNull) && + !Arg4->getType()->getPointeeOrArrayElementType()->isClkEventT()) { + Diag(TheCall->getArg(4)->getBeginLoc(), + diag::err_opencl_builtin_expected_type) + << TheCall->getDirectCallee() + << Context.getPointerType(Context.OCLClkEventTy); + return true; + } + + // Sixth argument is always passed as a pointer to clk_event_t. + if (!Arg5->isNullPointerConstant(Context, + Expr::NPC_ValueDependentIsNotNull) && + !(Arg5->getType()->isPointerType() && + Arg5->getType()->getPointeeType()->isClkEventT())) { + Diag(TheCall->getArg(5)->getBeginLoc(), + diag::err_opencl_builtin_expected_type) + << TheCall->getDirectCallee() + << Context.getPointerType(Context.OCLClkEventTy); + return true; + } + + if (NumArgs == 7) + return false; + + return checkOpenCLEnqueueVariadicArgs(SemaRef, TheCall, Arg6, 7); + } + + // None of the specific case has been detected, give generic error + Diag(TheCall->getBeginLoc(), diag::err_opencl_enqueue_kernel_incorrect_args); + return true; +} + +/// Returns OpenCL access qual. +static OpenCLAccessAttr *getOpenCLArgAccess(const Decl *D) { + return D->getAttr(); +} + +/// Returns true if pipe element type is different from the pointer. +static bool checkPipeArg(Sema &S, CallExpr *Call) { + const Expr *Arg0 = Call->getArg(0); + // First argument type should always be pipe. + if (!Arg0->getType()->isPipeType()) { + S.Diag(Call->getBeginLoc(), diag::err_opencl_builtin_pipe_first_arg) + << Call->getDirectCallee() << Arg0->getSourceRange(); + return true; + } + OpenCLAccessAttr *AccessQual = + getOpenCLArgAccess(cast(Arg0)->getDecl()); + // Validates the access qualifier is compatible with the call. + // OpenCL v2.0 s6.13.16 - The access qualifiers for pipe should only be + // read_only and write_only, and assumed to be read_only if no qualifier is + // specified. + switch (Call->getDirectCallee()->getBuiltinID()) { + case Builtin::BIread_pipe: + case Builtin::BIreserve_read_pipe: + case Builtin::BIcommit_read_pipe: + case Builtin::BIwork_group_reserve_read_pipe: + case Builtin::BIsub_group_reserve_read_pipe: + case Builtin::BIwork_group_commit_read_pipe: + case Builtin::BIsub_group_commit_read_pipe: + if (!(!AccessQual || AccessQual->isReadOnly())) { + S.Diag(Arg0->getBeginLoc(), + diag::err_opencl_builtin_pipe_invalid_access_modifier) + << "read_only" << Arg0->getSourceRange(); + return true; + } + break; + case Builtin::BIwrite_pipe: + case Builtin::BIreserve_write_pipe: + case Builtin::BIcommit_write_pipe: + case Builtin::BIwork_group_reserve_write_pipe: + case Builtin::BIsub_group_reserve_write_pipe: + case Builtin::BIwork_group_commit_write_pipe: + case Builtin::BIsub_group_commit_write_pipe: + if (!(AccessQual && AccessQual->isWriteOnly())) { + S.Diag(Arg0->getBeginLoc(), + diag::err_opencl_builtin_pipe_invalid_access_modifier) + << "write_only" << Arg0->getSourceRange(); + return true; + } + break; + default: + break; + } + return false; +} + +/// Returns true if pipe element type is different from the pointer. +static bool checkPipePacketType(Sema &S, CallExpr *Call, unsigned Idx) { + const Expr *Arg0 = Call->getArg(0); + const Expr *ArgIdx = Call->getArg(Idx); + const PipeType *PipeTy = cast(Arg0->getType()); + const QualType EltTy = PipeTy->getElementType(); + const PointerType *ArgTy = ArgIdx->getType()->getAs(); + // The Idx argument should be a pointer and the type of the pointer and + // the type of pipe element should also be the same. + if (!ArgTy || + !S.Context.hasSameType( + EltTy, ArgTy->getPointeeType()->getCanonicalTypeInternal())) { + S.Diag(Call->getBeginLoc(), diag::err_opencl_builtin_pipe_invalid_arg) + << Call->getDirectCallee() << S.Context.getPointerType(EltTy) + << ArgIdx->getType() << ArgIdx->getSourceRange(); + return true; + } + return false; +} + +bool SemaOpenCL::checkBuiltinRWPipe(CallExpr *Call) { + // OpenCL v2.0 s6.13.16.2 - The built-in read/write + // functions have two forms. + switch (Call->getNumArgs()) { + case 2: + if (checkPipeArg(SemaRef, Call)) + return true; + // The call with 2 arguments should be + // read/write_pipe(pipe T, T*). + // Check packet type T. + if (checkPipePacketType(SemaRef, Call, 1)) + return true; + break; + + case 4: { + if (checkPipeArg(SemaRef, Call)) + return true; + // The call with 4 arguments should be + // read/write_pipe(pipe T, reserve_id_t, uint, T*). + // Check reserve_id_t. + if (!Call->getArg(1)->getType()->isReserveIDT()) { + Diag(Call->getBeginLoc(), diag::err_opencl_builtin_pipe_invalid_arg) + << Call->getDirectCallee() << getASTContext().OCLReserveIDTy + << Call->getArg(1)->getType() << Call->getArg(1)->getSourceRange(); + return true; + } + + // Check the index. + const Expr *Arg2 = Call->getArg(2); + if (!Arg2->getType()->isIntegerType() && + !Arg2->getType()->isUnsignedIntegerType()) { + Diag(Call->getBeginLoc(), diag::err_opencl_builtin_pipe_invalid_arg) + << Call->getDirectCallee() << getASTContext().UnsignedIntTy + << Arg2->getType() << Arg2->getSourceRange(); + return true; + } + + // Check packet type T. + if (checkPipePacketType(SemaRef, Call, 3)) + return true; + } break; + default: + Diag(Call->getBeginLoc(), diag::err_opencl_builtin_pipe_arg_num) + << Call->getDirectCallee() << Call->getSourceRange(); + return true; + } + + return false; +} + +bool SemaOpenCL::checkBuiltinReserveRWPipe(CallExpr *Call) { + if (SemaRef.checkArgCount(Call, 2)) + return true; + + if (checkPipeArg(SemaRef, Call)) + return true; + + // Check the reserve size. + if (!Call->getArg(1)->getType()->isIntegerType() && + !Call->getArg(1)->getType()->isUnsignedIntegerType()) { + Diag(Call->getBeginLoc(), diag::err_opencl_builtin_pipe_invalid_arg) + << Call->getDirectCallee() << getASTContext().UnsignedIntTy + << Call->getArg(1)->getType() << Call->getArg(1)->getSourceRange(); + return true; + } + + // Since return type of reserve_read/write_pipe built-in function is + // reserve_id_t, which is not defined in the builtin def file , we used int + // as return type and need to override the return type of these functions. + Call->setType(getASTContext().OCLReserveIDTy); + + return false; +} + +bool SemaOpenCL::checkBuiltinCommitRWPipe(CallExpr *Call) { + if (SemaRef.checkArgCount(Call, 2)) + return true; + + if (checkPipeArg(SemaRef, Call)) + return true; + + // Check reserve_id_t. + if (!Call->getArg(1)->getType()->isReserveIDT()) { + Diag(Call->getBeginLoc(), diag::err_opencl_builtin_pipe_invalid_arg) + << Call->getDirectCallee() << getASTContext().OCLReserveIDTy + << Call->getArg(1)->getType() << Call->getArg(1)->getSourceRange(); + return true; + } + + return false; +} + +bool SemaOpenCL::checkBuiltinPipePackets(CallExpr *Call) { + if (SemaRef.checkArgCount(Call, 1)) + return true; + + if (!Call->getArg(0)->getType()->isPipeType()) { + Diag(Call->getBeginLoc(), diag::err_opencl_builtin_pipe_first_arg) + << Call->getDirectCallee() << Call->getArg(0)->getSourceRange(); + return true; + } + + return false; +} + +bool SemaOpenCL::checkBuiltinToAddr(unsigned BuiltinID, CallExpr *Call) { + if (SemaRef.checkArgCount(Call, 1)) + return true; + + auto RT = Call->getArg(0)->getType(); + if (!RT->isPointerType() || + RT->getPointeeType().getAddressSpace() == LangAS::opencl_constant) { + Diag(Call->getBeginLoc(), diag::err_opencl_builtin_to_addr_invalid_arg) + << Call->getArg(0) << Call->getDirectCallee() << Call->getSourceRange(); + return true; + } + + if (RT->getPointeeType().getAddressSpace() != LangAS::opencl_generic) { + Diag(Call->getArg(0)->getBeginLoc(), + diag::warn_opencl_generic_address_space_arg) + << Call->getDirectCallee()->getNameInfo().getAsString() + << Call->getArg(0)->getSourceRange(); + } + + RT = RT->getPointeeType(); + auto Qual = RT.getQualifiers(); + switch (BuiltinID) { + case Builtin::BIto_global: + Qual.setAddressSpace(LangAS::opencl_global); + break; + case Builtin::BIto_local: + Qual.setAddressSpace(LangAS::opencl_local); + break; + case Builtin::BIto_private: + Qual.setAddressSpace(LangAS::opencl_private); + break; + default: + llvm_unreachable("Invalid builtin function"); + } + Call->setType(getASTContext().getPointerType( + getASTContext().getQualifiedType(RT.getUnqualifiedType(), Qual))); + + return false; +} + } // namespace clang