//===--- LinearLifetimeCheckerPrivate.h -----------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #ifndef SWIFT_SIL_LINEARLIFETIMECHECKER_PRIVATE_H #define SWIFT_SIL_LINEARLIFETIMECHECKER_PRIVATE_H #include "swift/SIL/LinearLifetimeChecker.h" #include "llvm/Support/ErrorHandling.h" namespace swift { struct LLVM_LIBRARY_VISIBILITY LinearLifetimeChecker::ErrorBehaviorKind { enum inner_t { Invalid = 0, ReturnFalse = 1, PrintMessage = 2, Assert = 4, ReturnFalseOnLeak = 8, StoreNonConsumingUsesOutsideLifetime = 16, PrintMessageAndReturnFalse = PrintMessage | ReturnFalse, PrintMessageAndAssert = PrintMessage | Assert, ReturnFalseOnLeakAssertOtherwise = ReturnFalseOnLeak | Assert, } Value; ErrorBehaviorKind() : Value(Invalid) {} ErrorBehaviorKind(inner_t Inner) : Value(Inner) { assert(Value != Invalid); } ErrorBehaviorKind(unsigned Inner) : Value(inner_t(Inner)) { assert(Value != Invalid); } bool shouldStoreNonConsumingUsesOutsideLifetime() const { assert(Value != Invalid); return Value & StoreNonConsumingUsesOutsideLifetime; } bool shouldAssert() const { assert(Value != Invalid); return Value & Assert; } bool shouldReturnFalseOnLeak() const { assert(Value != Invalid); return Value & ReturnFalseOnLeak; } bool shouldPrintMessage() const { assert(Value != Invalid); return Value & PrintMessage; } bool shouldReturnFalse() const { assert(Value != Invalid); return Value & ReturnFalse; } }; class LLVM_LIBRARY_VISIBILITY LinearLifetimeChecker::Error { friend class ErrorBuilder; bool foundUseAfterFree = false; bool foundLeak = false; bool foundOverConsume = false; bool foundUseOutsideOfLifetime = false; public: Error() {} bool getFoundError() const { return foundUseAfterFree || foundLeak || foundOverConsume || foundUseOutsideOfLifetime; } bool getFoundLeak() const { return foundLeak; } bool getFoundUseAfterFree() const { return foundUseAfterFree; } bool getFoundOverConsume() const { return foundOverConsume; } bool getFoundUseOutsideOfLifetime() const { return foundUseOutsideOfLifetime; } }; class LLVM_LIBRARY_VISIBILITY LinearLifetimeChecker::ErrorBuilder { StringRef functionName; ErrorBehaviorKind behavior; std::optional error; unsigned *errorMessageCounter; public: ErrorBuilder(const SILFunction &fn, LinearLifetimeChecker::ErrorBehaviorKind behavior, unsigned *errorMessageCounter = nullptr) : functionName(fn.getName()), behavior(behavior), error(Error()), errorMessageCounter(errorMessageCounter) {} ErrorBuilder(const SILFunction &fn, LinearLifetimeChecker::ErrorBehaviorKind::inner_t behavior, unsigned *errorMessageCounter = nullptr) : ErrorBuilder(fn, LinearLifetimeChecker::ErrorBehaviorKind(behavior), errorMessageCounter) {} ErrorBuilder(const ErrorBuilder &other) : functionName(other.functionName), behavior(other.behavior), error(other.error), errorMessageCounter(other.errorMessageCounter) {} ErrorBuilder &operator=(const ErrorBuilder &other) { functionName = other.functionName; behavior = other.behavior; error = other.error; errorMessageCounter = other.errorMessageCounter; return *this; } Error consumeAndGetFinalError() && { auto result = *error; error = std::nullopt; errorMessageCounter = nullptr; return result; } void tryDumpErrorCounter() const { if (!errorMessageCounter) { return; } llvm::errs() << "Error#: " << *errorMessageCounter << ". "; } void tryIncrementErrorCounter() { if (!errorMessageCounter) { return; } ++(*errorMessageCounter); } bool handleLeak(llvm::function_ref &&messagePrinterFunc) { error->foundLeak = true; if (behavior.shouldPrintMessage()) { tryDumpErrorCounter(); llvm::errs() << "Begin Error in Function: '" << functionName << "'\n"; messagePrinterFunc(); tryDumpErrorCounter(); llvm::errs() << "End Error in Function: '" << functionName << "'\n"; tryIncrementErrorCounter(); } if (behavior.shouldReturnFalseOnLeak()) { return false; } // We already printed out our error if we needed to, so don't pass it along. return handleError([]() {}, true); } bool handleOverConsume(llvm::function_ref &&messagePrinterFunc) { error->foundOverConsume = true; return handleError(std::move(messagePrinterFunc)); } bool handleUseAfterFree(llvm::function_ref &&messagePrinterFunc) { error->foundUseAfterFree = true; return handleError(std::move(messagePrinterFunc)); } bool handleMalformedSIL(llvm::function_ref &&messagePrinterFunc) const { return handleError(std::move(messagePrinterFunc)); } /// Handle a case where we either found a use-after-free due to a /// non-consuming use after our lifetime has ended /or/ if we found a use /// before def of a non consuming value. void handleUseOutsideOfLifetime(llvm::function_ref &&messagePrinterFunc) { error->foundUseOutsideOfLifetime = true; handleError(std::move(messagePrinterFunc)); } private: bool handleError(llvm::function_ref &&messagePrinterFunc, bool quiet = false) const { if (behavior.shouldPrintMessage()) { if (!quiet) { tryDumpErrorCounter(); llvm::errs() << "Begin Error in Function: '" << functionName << "'\n"; } messagePrinterFunc(); if (!quiet) { tryDumpErrorCounter(); llvm::errs() << "End Error in Function: '" << functionName << "'\n"; auto *self = const_cast(this); self->tryIncrementErrorCounter(); } } if (behavior.shouldReturnFalse()) { return false; } llvm::errs() << "Found ownership error?!\n"; llvm::report_fatal_error("triggering standard assertion failure routine"); } }; } // namespace swift #endif