diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -3611,13 +3611,15 @@ Clang provides builtins to support C++ standard library implementation of ``std::source_location`` as specified in C++20. With the exception -of ``__builtin_COLUMN``, these builtins are also implemented by GCC. +of ``__builtin_COLUMN`` and ``__builtin_FILE_NAME``, +these builtins are also implemented by GCC. **Syntax**: .. code-block:: c const char *__builtin_FILE(); + const char *__builtin_FILE_NAME(); // Clang only const char *__builtin_FUNCTION(); unsigned __builtin_LINE(); unsigned __builtin_COLUMN(); // Clang only @@ -3648,11 +3650,11 @@ **Description**: -The builtins ``__builtin_LINE``, ``__builtin_FUNCTION``, and ``__builtin_FILE`` -return the values, at the "invocation point", for ``__LINE__``, -``__FUNCTION__``, and ``__FILE__`` respectively. ``__builtin_COLUMN`` similarly -returns the column, though there is no corresponding macro. These builtins are -constant expressions. +The builtins ``__builtin_LINE``, ``__builtin_FUNCTION``, ``__builtin_FILE`` and +``__builtin_FILE_NAME`` return the values, at the "invocation point", for +``__LINE__``, ``__FUNCTION__``, ``__FILE__`` and ``__FILE_NAME__`` respectively. +``__builtin_COLUMN`` similarly returns the column, +though there is no corresponding macro. These builtins are constant expressions. When the builtins appear as part of a default function argument the invocation point is the location of the caller. When the builtins appear as part of a diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -116,6 +116,9 @@ optimizations. - Clang now supports ``__builtin_nondeterministic_value`` that returns a nondeterministic value of the same type as the provided argument. +- Clang now supports ``__builtin_FILE_NAME()`` which returns the same + information as the ``__FILE_NAME__`` macro (the presumed file name + from the invocation point, with no path components included). New Compiler Flags ------------------ diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -4685,13 +4685,14 @@ }; /// Represents a function call to one of __builtin_LINE(), __builtin_COLUMN(), -/// __builtin_FUNCTION(), __builtin_FILE(), or __builtin_source_location(). +/// __builtin_FUNCTION(), __builtin_FILE(), __builtin_FILE_NAME(), +/// or __builtin_source_location(). class SourceLocExpr final : public Expr { SourceLocation BuiltinLoc, RParenLoc; DeclContext *ParentContext; public: - enum IdentKind { Function, File, Line, Column, SourceLocStruct }; + enum IdentKind { Function, File, FileName, Line, Column, SourceLocStruct }; SourceLocExpr(const ASTContext &Ctx, IdentKind Type, QualType ResultTy, SourceLocation BLoc, SourceLocation RParenLoc, @@ -4715,6 +4716,7 @@ bool isIntType() const { switch (getIdentKind()) { case File: + case FileName: case Function: case SourceLocStruct: return false; diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -436,6 +436,7 @@ KEYWORD(__builtin_choose_expr , KEYALL) KEYWORD(__builtin_offsetof , KEYALL) KEYWORD(__builtin_FILE , KEYALL) +KEYWORD(__builtin_FILE_NAME , KEYALL) KEYWORD(__builtin_FUNCTION , KEYALL) KEYWORD(__builtin_LINE , KEYALL) KEYWORD(__builtin_COLUMN , KEYALL) diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -2842,6 +2842,11 @@ const LangOptions &LangOpts, const TargetInfo &TI); + static void processPathToFileName(SmallVectorImpl &FileName, + const PresumedLoc &PLoc, + const LangOptions &LangOpts, + const TargetInfo &TI); + private: void emitMacroDeprecationWarning(const Token &Identifier) const; void emitRestrictExpansionWarning(const Token &Identifier) const; diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -2239,6 +2239,8 @@ switch (getIdentKind()) { case File: return "__builtin_FILE"; + case FileName: + return "__builtin_FILE_NAME"; case Function: return "__builtin_FUNCTION"; case Line: @@ -2277,6 +2279,14 @@ }; switch (getIdentKind()) { + case SourceLocExpr::FileName: { + // __builtin_FILE_NAME() is a Clang-specific extension that expands to the + // the last part of __builtin_FILE(). + SmallString<256> FileName; + clang::Preprocessor::processPathToFileName( + FileName, PLoc, Ctx.getLangOpts(), Ctx.getTargetInfo()); + return MakeStringLiteral(FileName); + } case SourceLocExpr::File: { SmallString<256> Path(PLoc.getFilename()); clang::Preprocessor::processPathForFileMacro(Path, Ctx.getLangOpts(), diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -1559,17 +1559,11 @@ // __FILE_NAME__ is a Clang-specific extension that expands to the // the last part of __FILE__. if (II == Ident__FILE_NAME__) { - // Try to get the last path component, failing that return the original - // presumed location. - StringRef PLFileName = llvm::sys::path::filename(PLoc.getFilename()); - if (PLFileName != "") - FN += PLFileName; - else - FN += PLoc.getFilename(); + processPathToFileName(FN, PLoc, getLangOpts(), getTargetInfo()); } else { FN += PLoc.getFilename(); + processPathForFileMacro(FN, getLangOpts(), getTargetInfo()); } - processPathForFileMacro(FN, getLangOpts(), getTargetInfo()); Lexer::Stringify(FN); OS << '"' << FN << '"'; } @@ -1974,3 +1968,16 @@ llvm::sys::path::remove_dots(Path, false, llvm::sys::path::Style::posix); } } + +void Preprocessor::processPathToFileName(SmallVectorImpl &FileName, + const PresumedLoc &PLoc, + const LangOptions &LangOpts, + const TargetInfo &TI) { + // Try to get the last path component, failing that return the original + // presumed location. + StringRef PLFileName = llvm::sys::path::filename(PLoc.getFilename()); + if (PLFileName.empty()) + PLFileName = PLoc.getFilename(); + FileName.append(PLFileName.begin(), PLFileName.end()); + processPathForFileMacro(FileName, LangOpts, TI); +} diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -789,6 +789,7 @@ /// [GNU] '__builtin_choose_expr' '(' assign-expr ',' assign-expr ',' /// assign-expr ')' /// [GNU] '__builtin_FILE' '(' ')' +/// [CLANG] '__builtin_FILE_NAME' '(' ')' /// [GNU] '__builtin_FUNCTION' '(' ')' /// [GNU] '__builtin_LINE' '(' ')' /// [CLANG] '__builtin_COLUMN' '(' ')' @@ -1317,6 +1318,7 @@ case tok::kw___builtin_convertvector: case tok::kw___builtin_COLUMN: case tok::kw___builtin_FILE: + case tok::kw___builtin_FILE_NAME: case tok::kw___builtin_FUNCTION: case tok::kw___builtin_LINE: case tok::kw___builtin_source_location: @@ -2542,6 +2544,7 @@ /// assign-expr ')' /// [GNU] '__builtin_types_compatible_p' '(' type-name ',' type-name ')' /// [GNU] '__builtin_FILE' '(' ')' +/// [CLANG] '__builtin_FILE_NAME' '(' ')' /// [GNU] '__builtin_FUNCTION' '(' ')' /// [GNU] '__builtin_LINE' '(' ')' /// [CLANG] '__builtin_COLUMN' '(' ')' @@ -2777,6 +2780,7 @@ } case tok::kw___builtin_COLUMN: case tok::kw___builtin_FILE: + case tok::kw___builtin_FILE_NAME: case tok::kw___builtin_FUNCTION: case tok::kw___builtin_LINE: case tok::kw___builtin_source_location: { @@ -2790,6 +2794,8 @@ switch (T) { case tok::kw___builtin_FILE: return SourceLocExpr::File; + case tok::kw___builtin_FILE_NAME: + return SourceLocExpr::FileName; case tok::kw___builtin_FUNCTION: return SourceLocExpr::Function; case tok::kw___builtin_LINE: diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -17060,6 +17060,7 @@ QualType ResultTy; switch (Kind) { case SourceLocExpr::File: + case SourceLocExpr::FileName: case SourceLocExpr::Function: { QualType ArrTy = Context.getStringLiteralArrayType(Context.CharTy, 0); ResultTy = diff --git a/clang/test/Preprocessor/feature_tests.c b/clang/test/Preprocessor/feature_tests.c --- a/clang/test/Preprocessor/feature_tests.c +++ b/clang/test/Preprocessor/feature_tests.c @@ -24,6 +24,7 @@ // still return true. #if !__has_builtin(__builtin_LINE) || \ !__has_builtin(__builtin_FILE) || \ + !__has_builtin(__builtin_FILE_NAME) || \ !__has_builtin(__builtin_FUNCTION) || \ !__has_builtin(__builtin_COLUMN) || \ !__has_builtin(__builtin_types_compatible_p) diff --git a/clang/test/Preprocessor/feature_tests.cpp b/clang/test/Preprocessor/feature_tests.cpp --- a/clang/test/Preprocessor/feature_tests.cpp +++ b/clang/test/Preprocessor/feature_tests.cpp @@ -23,6 +23,7 @@ // still return true. #if !__has_builtin(__builtin_LINE) || \ !__has_builtin(__builtin_FILE) || \ + !__has_builtin(__builtin_FILE_NAME) || \ !__has_builtin(__builtin_FUNCTION) || \ !__has_builtin(__builtin_COLUMN) || \ !__has_builtin(__array_rank) || \ diff --git a/clang/test/Sema/source_location.c b/clang/test/Sema/source_location.c --- a/clang/test/Sema/source_location.c +++ b/clang/test/Sema/source_location.c @@ -17,12 +17,14 @@ #ifdef CONST_STRINGS _Static_assert(IsEqual(__builtin_FILE(), __FILE__), ""); +_Static_assert(IsEqual(__builtin_FILE_NAME(), __FILE_NAME__), ""); _Static_assert(__builtin_LINE() == __LINE__, ""); _Static_assert(IsEqual("", __builtin_FUNCTION()), ""); #line 42 "my_file.c" _Static_assert(__builtin_LINE() == 42, ""); _Static_assert(IsEqual(__builtin_FILE(), "my_file.c"), ""); +_Static_assert(IsEqual(__builtin_FILE_NAME(), "my_file.c"), ""); _Static_assert(__builtin_COLUMN() == __builtin_strlen("_Static_assert(_"), ""); diff --git a/clang/test/SemaCXX/Inputs/source-location-file.h b/clang/test/SemaCXX/Inputs/source-location-file.h --- a/clang/test/SemaCXX/Inputs/source-location-file.h +++ b/clang/test/SemaCXX/Inputs/source-location-file.h @@ -4,8 +4,10 @@ namespace source_location_file { constexpr const char *FILE = __FILE__; +constexpr const char *FILE_NAME = __FILE_NAME__; constexpr SL global_info = SL::current(); +constexpr const char *global_info_filename = __builtin_FILE_NAME(); constexpr SL test_function(SL v = SL::current()) { return v; @@ -15,6 +17,15 @@ return test_function(); } +constexpr const char *test_function_filename( + const char *file_name = __builtin_FILE_NAME()) { + return file_name; +} + +constexpr const char *test_function_filename_indirect() { + return test_function_filename(); +} + template constexpr U test_function_template(T, U u = U::current()) { return u; @@ -25,13 +36,29 @@ return test_function_template(t); } +template +constexpr U test_function_filename_template(T, U u = __builtin_FILE_NAME()) { + return u; +} + +template +constexpr U test_function_filename_template_indirect(T t) { + return test_function_filename_template(t); +} + struct TestClass { SL info = SL::current(); + const char *info_file_name = __builtin_FILE_NAME(); SL ctor_info; + const char *ctor_info_file_name = nullptr; TestClass() = default; - constexpr TestClass(int, SL cinfo = SL::current()) : ctor_info(cinfo) {} + constexpr TestClass(int, SL cinfo = SL::current(), + const char *cfile_name = __builtin_FILE_NAME()) : + ctor_info(cinfo), ctor_info_file_name(cfile_name) {} template - constexpr TestClass(int, T, U u = U::current()) : ctor_info(u) {} + constexpr TestClass(int, T, U u = U::current(), + const char *cfile_name = __builtin_FILE_NAME()) : + ctor_info(u), ctor_info_file_name(cfile_name) {} }; template @@ -39,6 +66,7 @@ int x; T info; T init_info = T::current(); + const char *init_info_file_name = __builtin_FILE_NAME(); }; } // namespace source_location_file diff --git a/clang/test/SemaCXX/source_location.cpp b/clang/test/SemaCXX/source_location.cpp --- a/clang/test/SemaCXX/source_location.cpp +++ b/clang/test/SemaCXX/source_location.cpp @@ -84,6 +84,7 @@ static_assert(is_same); static_assert(is_same); static_assert(is_same); +static_assert(is_same); static_assert(is_same); static_assert(is_same); @@ -91,6 +92,7 @@ static_assert(noexcept(__builtin_LINE())); static_assert(noexcept(__builtin_COLUMN())); static_assert(noexcept(__builtin_FILE())); +static_assert(noexcept(__builtin_FILE_NAME())); static_assert(noexcept(__builtin_FUNCTION())); static_assert(noexcept(__builtin_source_location())); @@ -346,6 +348,54 @@ } // namespace test_file +//===----------------------------------------------------------------------===// +// __builtin_FILE_NAME() +//===----------------------------------------------------------------------===// + +namespace test_file_name { +constexpr const char *test_file_name_simple( + const char *__f = __builtin_FILE_NAME()) { + return __f; +} +void test_function() { +#line 900 + static_assert(is_equal(test_file_name_simple(), __FILE_NAME__)); + static_assert(is_equal(SLF::test_function_filename(), __FILE_NAME__), ""); + static_assert(is_equal(SLF::test_function_filename_template(42), + __FILE_NAME__), ""); + + static_assert(is_equal(SLF::test_function_filename_indirect(), + SLF::global_info_filename), ""); + static_assert(is_equal(SLF::test_function_filename_template_indirect(42), + SLF::global_info_filename), ""); + + static_assert(test_file_name_simple() != nullptr); + static_assert(is_equal(test_file_name_simple(), "source_location.cpp")); +} + +void test_class() { +#line 315 + using SLF::TestClass; + constexpr TestClass Default; + constexpr TestClass InParam{42}; + constexpr TestClass Template{42, 42}; + constexpr auto *F = Default.info_file_name; + constexpr auto Char = F[0]; + static_assert(is_equal(Default.info_file_name, SLF::FILE_NAME), ""); + static_assert(is_equal(InParam.info_file_name, SLF::FILE_NAME), ""); + static_assert(is_equal(InParam.ctor_info_file_name, __FILE_NAME__), ""); +} + +void test_aggr_class() { + using Agg = SLF::AggrClass<>; + constexpr Agg Default{}; + constexpr Agg InitOne{42}; + static_assert(is_equal(Default.init_info_file_name, __FILE_NAME__), ""); + static_assert(is_equal(InitOne.init_info_file_name, __FILE_NAME__), ""); +} + +} // namespace test_file_name + //===----------------------------------------------------------------------===// // __builtin_FUNCTION() //===----------------------------------------------------------------------===// @@ -487,6 +537,7 @@ #line 44 "test_file.c" static_assert(is_equal("test_file.c", __FILE__)); static_assert(is_equal("test_file.c", __builtin_FILE())); +static_assert(is_equal("test_file.c", __builtin_FILE_NAME())); static_assert(is_equal("test_file.c", SL::current().file())); static_assert(is_equal("test_file.c", SLF::test_function().file())); static_assert(is_equal(SLF::FILE, SLF::test_function_indirect().file())); diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -259,6 +259,10 @@ Lang_CXX03, Verifier, functionDecl(hasDescendant( sourceLocExpr(hasBuiltinStr("__builtin_FILE"))))); + testImport("void declToImport() { (void)__builtin_FILE_NAME(); }", Lang_CXX03, + "", Lang_CXX03, Verifier, + functionDecl(hasDescendant( + sourceLocExpr(hasBuiltinStr("__builtin_FILE_NAME"))))); testImport("void declToImport() { (void)__builtin_COLUMN(); }", Lang_CXX03, "", Lang_CXX03, Verifier, functionDecl(hasDescendant(