jpayne@69: // Copyright (c) 2021 Cloudflare, Inc. and contributors jpayne@69: // Licensed under the MIT License: jpayne@69: // jpayne@69: // Permission is hereby granted, free of charge, to any person obtaining a copy jpayne@69: // of this software and associated documentation files (the "Software"), to deal jpayne@69: // in the Software without restriction, including without limitation the rights jpayne@69: // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell jpayne@69: // copies of the Software, and to permit persons to whom the Software is jpayne@69: // furnished to do so, subject to the following conditions: jpayne@69: // jpayne@69: // The above copyright notice and this permission notice shall be included in jpayne@69: // all copies or substantial portions of the Software. jpayne@69: // jpayne@69: // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR jpayne@69: // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, jpayne@69: // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE jpayne@69: // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER jpayne@69: // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, jpayne@69: // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN jpayne@69: // THE SOFTWARE. jpayne@69: jpayne@69: #pragma once jpayne@69: jpayne@69: #include "string.h" jpayne@69: jpayne@69: KJ_BEGIN_HEADER jpayne@69: jpayne@69: // GCC does not implement __builtin_COLUMN() as that's non-standard but MSVC & clang do. jpayne@69: // MSVC does as of version https://github.com/microsoft/STL/issues/54) but there's currently not any jpayne@69: // pressing need for this for MSVC & writing the write compiler version check is annoying. jpayne@69: // Checking for clang version is problematic due to the way that XCode lies about __clang_major__. jpayne@69: // Instead we use __has_builtin as the feature check to check clang. jpayne@69: // Context: https://github.com/capnproto/capnproto/issues/1305 jpayne@69: #ifdef __has_builtin jpayne@69: #if __has_builtin(__builtin_COLUMN) jpayne@69: #define KJ_CALLER_COLUMN() __builtin_COLUMN() jpayne@69: #else jpayne@69: #define KJ_CALLER_COLUMN() 0 jpayne@69: #endif jpayne@69: #else jpayne@69: #define KJ_CALLER_COLUMN() 0 jpayne@69: #endif jpayne@69: jpayne@69: #if KJ_CPP_STD > 201703L jpayne@69: #define KJ_COMPILER_SUPPORTS_SOURCE_LOCATION 1 jpayne@69: #elif defined(__has_builtin) jpayne@69: // Clang 9 added these builtins: https://releases.llvm.org/9.0.0/tools/clang/docs/LanguageExtensions.html jpayne@69: // Use __has_builtin as the way to detect this because __clang_major__ is unreliable (see above jpayne@69: // about issue with Xcode-provided clang). jpayne@69: #define KJ_COMPILER_SUPPORTS_SOURCE_LOCATION ( \ jpayne@69: __has_builtin(__builtin_FILE) && \ jpayne@69: __has_builtin(__builtin_LINE) && \ jpayne@69: __has_builtin(__builtin_FUNCTION) \ jpayne@69: ) jpayne@69: #elif __GNUC__ >= 5 jpayne@69: // GCC 5 supports the required builtins: https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gcc/Other-Builtins.html jpayne@69: #define KJ_COMPILER_SUPPORTS_SOURCE_LOCATION 1 jpayne@69: #endif jpayne@69: jpayne@69: namespace kj { jpayne@69: class SourceLocation { jpayne@69: // libc++ doesn't seem to implement (or even ), so jpayne@69: // this is a non-STL wrapper over the compiler primitives (these are the same across MSVC/clang/ jpayne@69: // gcc). Additionally this uses kj::StringPtr for holding the strings instead of const char* which jpayne@69: // makes it integrate a little more nicely into KJ. jpayne@69: jpayne@69: struct Badge { explicit constexpr Badge() = default; }; jpayne@69: // Neat little trick to make sure we can never call SourceLocation with explicit arguments. jpayne@69: public: jpayne@69: #if !KJ_COMPILER_SUPPORTS_SOURCE_LOCATION jpayne@69: constexpr SourceLocation() : fileName("??"), function("??"), lineNumber(0), columnNumber(0) {} jpayne@69: // Constructs a dummy source location that's not pointing at anything. jpayne@69: #else jpayne@69: constexpr SourceLocation(Badge = Badge{}, const char* file = __builtin_FILE(), jpayne@69: const char* func = __builtin_FUNCTION(), uint line = __builtin_LINE(), jpayne@69: uint column = KJ_CALLER_COLUMN()) jpayne@69: : fileName(file), function(func), lineNumber(line), columnNumber(column) jpayne@69: {} jpayne@69: #endif jpayne@69: jpayne@69: #if KJ_COMPILER_SUPPORTS_SOURCE_LOCATION jpayne@69: // This can only be exposed if we actually generate valid SourceLocation objects as otherwise all jpayne@69: // SourceLocation objects would confusingly (and likely problematically) be equated equal. jpayne@69: constexpr bool operator==(const SourceLocation& o) const { jpayne@69: // Pointer equality is fine here based on how SourceLocation operates & how compilers will jpayne@69: // intern all duplicate string constants. jpayne@69: return fileName == o.fileName && function == o.function && lineNumber == o.lineNumber && jpayne@69: columnNumber == o.columnNumber; jpayne@69: } jpayne@69: #endif jpayne@69: jpayne@69: const char* fileName; jpayne@69: const char* function; jpayne@69: uint lineNumber; jpayne@69: uint columnNumber; jpayne@69: }; jpayne@69: jpayne@69: kj::String KJ_STRINGIFY(const SourceLocation& l); jpayne@69: jpayne@69: class NoopSourceLocation { jpayne@69: // This is used in places where we want to conditionally compile out tracking the source location. jpayne@69: // As such it intentionally lacks all the features but the default constructor so that the API jpayne@69: // isn't accidentally used in the wrong compilation context. jpayne@69: }; jpayne@69: jpayne@69: KJ_UNUSED static kj::String KJ_STRINGIFY(const NoopSourceLocation& l) { jpayne@69: return kj::String(); jpayne@69: } jpayne@69: } // namespace kj jpayne@69: jpayne@69: KJ_END_HEADER