diff options
Diffstat (limited to 'clang/lib/Sema/SemaDeclAttr.cpp')
-rw-r--r-- | clang/lib/Sema/SemaDeclAttr.cpp | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 7cfdb9e286b..84ae677d4db 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -3480,6 +3480,144 @@ static void handleFormatAttr(Sema &S, Decl *D, const ParsedAttr &AL) { D->addAttr(NewAttr); } +/// Handle __attribute__((callback(CalleeIdx, PayloadIdx0, ...))) attributes. +static void handleCallbackAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + // The index that identifies the callback callee is mandatory. + if (AL.getNumArgs() == 0) { + S.Diag(AL.getLoc(), diag::err_callback_attribute_no_callee) + << AL.getRange(); + return; + } + + bool HasImplicitThisParam = isInstanceMethod(D); + int32_t NumArgs = getFunctionOrMethodNumParams(D); + + FunctionDecl *FD = D->getAsFunction(); + assert(FD && "Expected a function declaration!"); + + llvm::StringMap<int> NameIdxMapping; + NameIdxMapping["__"] = -1; + + NameIdxMapping["this"] = 0; + + int Idx = 1; + for (const ParmVarDecl *PVD : FD->parameters()) + NameIdxMapping[PVD->getName()] = Idx++; + + auto UnknownName = NameIdxMapping.end(); + + SmallVector<int, 8> EncodingIndices; + for (unsigned I = 0, E = AL.getNumArgs(); I < E; ++I) { + SourceRange SR; + int32_t ArgIdx; + + if (AL.isArgIdent(I)) { + IdentifierLoc *IdLoc = AL.getArgAsIdent(I); + auto It = NameIdxMapping.find(IdLoc->Ident->getName()); + if (It == UnknownName) { + S.Diag(AL.getLoc(), diag::err_callback_attribute_argument_unknown) + << IdLoc->Ident << IdLoc->Loc; + return; + } + + SR = SourceRange(IdLoc->Loc); + ArgIdx = It->second; + } else if (AL.isArgExpr(I)) { + Expr *IdxExpr = AL.getArgAsExpr(I); + + // If the expression is not parseable as an int32_t we have a problem. + if (!checkUInt32Argument(S, AL, IdxExpr, (uint32_t &)ArgIdx, I + 1, + false)) { + S.Diag(AL.getLoc(), diag::err_attribute_argument_out_of_bounds) + << AL << (I + 1) << IdxExpr->getSourceRange(); + return; + } + + // Check oob, excluding the special values, 0 and -1. + if (ArgIdx < -1 || ArgIdx > NumArgs) { + S.Diag(AL.getLoc(), diag::err_attribute_argument_out_of_bounds) + << AL << (I + 1) << IdxExpr->getSourceRange(); + return; + } + + SR = IdxExpr->getSourceRange(); + } else { + llvm_unreachable("Unexpected ParsedAttr argument type!"); + } + + if (ArgIdx == 0 && !HasImplicitThisParam) { + S.Diag(AL.getLoc(), diag::err_callback_implicit_this_not_available) + << (I + 1) << SR; + return; + } + + // Adjust for the case we do not have an implicit "this" parameter. In this + // case we decrease all positive values by 1 to get LLVM argument indices. + if (!HasImplicitThisParam && ArgIdx > 0) + ArgIdx -= 1; + + EncodingIndices.push_back(ArgIdx); + } + + int CalleeIdx = EncodingIndices.front(); + // Check if the callee index is proper, thus not "this" and not "unknown". + if (CalleeIdx < HasImplicitThisParam) { + S.Diag(AL.getLoc(), diag::err_callback_attribute_invalid_callee) + << AL.getRange(); + return; + } + + // Get the callee type, note the index adjustment as the AST doesn't contain + // the this type (which the callee cannot reference anyway!). + const Type *CalleeType = + getFunctionOrMethodParamType(D, CalleeIdx - HasImplicitThisParam) + .getTypePtr(); + if (!CalleeType || !CalleeType->isFunctionPointerType()) { + S.Diag(AL.getLoc(), diag::err_callback_callee_no_function_type) + << AL.getRange(); + return; + } + + const Type *CalleeFnType = + CalleeType->getPointeeType()->getUnqualifiedDesugaredType(); + + // TODO: Check the type of the callee arguments. + + const auto *CalleeFnProtoType = dyn_cast<FunctionProtoType>(CalleeFnType); + if (!CalleeFnProtoType) { + S.Diag(AL.getLoc(), diag::err_callback_callee_no_function_type) + << AL.getRange(); + return; + } + + if (CalleeFnProtoType->getNumParams() > EncodingIndices.size() - 1) { + S.Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) + << AL << (unsigned)(EncodingIndices.size() - 1); + return; + } + + if (CalleeFnProtoType->getNumParams() < EncodingIndices.size() - 1) { + S.Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) + << AL << (unsigned)(EncodingIndices.size() - 1); + return; + } + + if (CalleeFnProtoType->isVariadic()) { + S.Diag(AL.getLoc(), diag::err_callback_callee_is_variadic) << AL.getRange(); + return; + } + + // Do not allow multiple callback attributes. + if (D->hasAttr<CallbackAttr>()) { + S.Diag(AL.getLoc(), diag::err_callback_attribute_multiple) << AL.getRange(); + return; + } + + D->addAttr(::new (S.Context) CallbackAttr( + AL.getRange(), S.Context, EncodingIndices.data(), EncodingIndices.size(), + AL.getAttributeSpellingListIndex())); +} + static void handleTransparentUnionAttr(Sema &S, Decl *D, const ParsedAttr &AL) { // Try to find the underlying union declaration. RecordDecl *RD = nullptr; @@ -6451,6 +6589,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case ParsedAttr::AT_FormatArg: handleFormatArgAttr(S, D, AL); break; + case ParsedAttr::AT_Callback: + handleCallbackAttr(S, D, AL); + break; case ParsedAttr::AT_CUDAGlobal: handleGlobalAttr(S, D, AL); break; |