================ @@ -0,0 +1,1572 @@ +//=== SemaFunctionEffects.cpp - Sema handling of function effects ---------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements Sema handling of function effects. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/Type.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Sema/SemaInternal.h" + +#define DEBUG_TYPE "effectanalysis" + +using namespace clang; + +namespace { + +enum class ViolationID : uint8_t { + None = 0, // Sentinel for an empty Violation. + // These first few map to a %select{} in a diagnostic. + BaseDiagnosticIndex, + AllocatesMemory = BaseDiagnosticIndex, + ThrowsOrCatchesExceptions, + HasStaticLocalVariable, + AccessesThreadLocalVariable, + AccessesObjCMethodOrProperty, + + // These only apply to callees, where the analysis stops at the Decl. + DeclDisallowsInference, + + // These both apply to indirect calls. The difference is that sometimes + // we have an actual Decl (generally a variable) which is the function + // pointer being called, and sometimes, typically due to a cast, we only + // have an expression. + CallsDeclWithoutEffect, + CallsExprWithoutEffect, +}; + +// Information about the AST context in which a violation was found, so +// that diagnostics can point to the correct source. +class ViolationSite { +public: + enum class Kind : uint8_t { + Default = 0, // Function body. + MemberInitializer = 1, + DefaultArgExpr = 2 + }; + +private: + llvm::PointerIntPair<CXXDefaultArgExpr *, 2, Kind> Impl; + +public: + ViolationSite() = default; + + explicit ViolationSite(CXXDefaultArgExpr *E) + : Impl(E, Kind::DefaultArgExpr) {} + + Kind kind() const { return static_cast<Kind>(Impl.getInt()); } + CXXDefaultArgExpr *defaultArgExpr() const { return Impl.getPointer(); } + + void setKind(Kind K) { Impl.setPointerAndInt(nullptr, K); } +}; + +// Represents a violation of the rules, potentially for the entire duration of +// the analysis phase, in order to refer to it when explaining why a caller has +// been made unsafe by a callee. Can be transformed into either a Diagnostic +// (warning or a note), depending on whether the violation pertains to a +// function failing to be verifed as holding an effect vs. a function failing to +// be inferred as holding that effect. +struct Violation { + FunctionEffect Effect; + FunctionEffect + CalleeEffectPreventingInference; // Only for certain IDs; can be None. + ViolationID ID = ViolationID::None; + ViolationSite Site; + SourceLocation Loc; + const Decl *Callee = nullptr; // Only valid for Calls*. + + Violation() = default; + + Violation(FunctionEffect Effect, ViolationID ID, ViolationSite VS, + SourceLocation Loc, const Decl *Callee = nullptr, + std::optional<FunctionEffect> CalleeEffect = std::nullopt) + : Effect(Effect), CalleeEffectPreventingInference( + CalleeEffect.value_or(FunctionEffect())), + ID(ID), Site(VS), Loc(Loc), Callee(Callee) {} + + unsigned diagnosticSelectIndex() const { + return unsigned(ID) - unsigned(ViolationID::BaseDiagnosticIndex); + } +}; + +enum class SpecialFuncType : uint8_t { None, OperatorNew, OperatorDelete }; +enum class CallableType : uint8_t { + // Unknown: probably function pointer. + Unknown, + Function, + Virtual, + Block +}; + +// Return whether a function's effects CAN be verified. +// The question of whether it SHOULD be verified is independent. +static bool functionIsVerifiable(const FunctionDecl *FD) { + if (FD->isTrivial()) { + // Otherwise `struct x { int a; };` would have an unverifiable default + // constructor. + return true; + } + return FD->hasBody(); +} + +static bool isNoexcept(const FunctionDecl *FD) { + const auto *FPT = FD->getType()->castAs<FunctionProtoType>(); + if (FPT->isNothrow() || FD->hasAttr<NoThrowAttr>()) + return true; + return false; +} + +// This list is probably incomplete. +// FIXME: Investigate: +// __builtin_eh_return? +// __builtin_allow_runtime_check? +// __builtin_unwind_init and other similar things that sound exception-related. +// va_copy? +// coroutines? +static FunctionEffectKindSet getBuiltinFunctionEffects(unsigned BuiltinID) { + FunctionEffectKindSet Result; + + switch (BuiltinID) { + case 0: // Not builtin. + default: // By default, builtins have no known effects. + break; + + // These allocate/deallocate heap memory. + case Builtin::ID::BI__builtin_calloc: + case Builtin::ID::BI__builtin_malloc: + case Builtin::ID::BI__builtin_realloc: + case Builtin::ID::BI__builtin_free: + case Builtin::ID::BI__builtin_operator_delete: + case Builtin::ID::BI__builtin_operator_new: + case Builtin::ID::BIaligned_alloc: + case Builtin::ID::BIcalloc: + case Builtin::ID::BImalloc: + case Builtin::ID::BImemalign: + case Builtin::ID::BIrealloc: + case Builtin::ID::BIfree: + + case Builtin::ID::BIfopen: + case Builtin::ID::BIpthread_create: + case Builtin::ID::BI_Block_object_dispose: + Result.insert(FunctionEffect(FunctionEffect::Kind::Allocating)); + break; + + // These block in some other way than allocating memory. + case Builtin::ID::BIlongjmp: + case Builtin::ID::BI_longjmp: + case Builtin::ID::BIsiglongjmp: + case Builtin::ID::BI__builtin_longjmp: + case Builtin::ID::BIobjc_exception_throw: + + // Objective-C runtime. + case Builtin::ID::BIobjc_msgSend: + case Builtin::ID::BIobjc_msgSend_fpret: + case Builtin::ID::BIobjc_msgSend_fp2ret: + case Builtin::ID::BIobjc_msgSend_stret: + case Builtin::ID::BIobjc_msgSendSuper: + case Builtin::ID::BIobjc_getClass: + case Builtin::ID::BIobjc_getMetaClass: + case Builtin::ID::BIobjc_enumerationMutation: + case Builtin::ID::BIobjc_assign_ivar: + case Builtin::ID::BIobjc_assign_global: + case Builtin::ID::BIobjc_sync_enter: + case Builtin::ID::BIobjc_sync_exit: + case Builtin::ID::BINSLog: + case Builtin::ID::BINSLogv: + + // stdio.h + case Builtin::ID::BIfread: + case Builtin::ID::BIfwrite: + + // stdio.h: printf family. + case Builtin::ID::BIprintf: + case Builtin::ID::BI__builtin_printf: + case Builtin::ID::BIfprintf: + case Builtin::ID::BIsnprintf: + case Builtin::ID::BIsprintf: + case Builtin::ID::BIvprintf: + case Builtin::ID::BIvfprintf: + case Builtin::ID::BIvsnprintf: + case Builtin::ID::BIvsprintf: + + // stdio.h: scanf family. + case Builtin::ID::BIscanf: + case Builtin::ID::BIfscanf: + case Builtin::ID::BIsscanf: + case Builtin::ID::BIvscanf: + case Builtin::ID::BIvfscanf: + case Builtin::ID::BIvsscanf: + ---------------- Sirraide wrote:
```suggestion ``` Spurious empty line https://github.com/llvm/llvm-project/pull/99656 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits