Index: clang/include/clang/AST/Expr.h =================================================================== --- clang/include/clang/AST/Expr.h +++ clang/include/clang/AST/Expr.h @@ -4686,13 +4686,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, @@ -4716,6 +4717,7 @@ bool isIntType() const { switch (getIdentKind()) { case File: + case FileName: case Function: case SourceLocStruct: return false; Index: clang/include/clang/Basic/TokenKinds.def =================================================================== --- clang/include/clang/Basic/TokenKinds.def +++ 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) Index: clang/include/clang/Lex/Preprocessor.h =================================================================== --- clang/include/clang/Lex/Preprocessor.h +++ clang/include/clang/Lex/Preprocessor.h @@ -2855,6 +2855,12 @@ const LangOptions &LangOpts, const TargetInfo &TI); + template + static void processPathToFilename(SmallString &FileName, + const PresumedLoc &PLoc, + const LangOptions &LangOpts, + const TargetInfo &TI); + private: void emitMacroDeprecationWarning(const Token &Identifier) const; void emitRestrictExpansionWarning(const Token &Identifier) const; Index: clang/lib/AST/Expr.cpp =================================================================== --- clang/lib/AST/Expr.cpp +++ 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(), Index: clang/lib/Lex/PPMacroExpansion.cpp =================================================================== --- clang/lib/Lex/PPMacroExpansion.cpp +++ 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 << '"'; } @@ -1995,3 +1989,18 @@ llvm::sys::path::remove_dots(Path, false, llvm::sys::path::Style::posix); } } + +template +void Preprocessor::processPathToFilename(SmallString &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 != "") + FileName += PLFileName; + else + FileName += PLoc.getFilename(); + processPathForFileMacro(FileName, LangOpts, TI); +} Index: clang/lib/Parse/ParseExpr.cpp =================================================================== --- clang/lib/Parse/ParseExpr.cpp +++ 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' '(' ')' @@ -801,20 +802,18 @@ /// [OBJC] '\@encode' '(' type-name ')' /// [OBJC] objc-string-literal /// [C++] simple-type-specifier '(' expression-list[opt] ')' [C++ 5.2.3] -/// [C++11] simple-type-specifier braced-init-list [C++11 5.2.3] -/// [C++] typename-specifier '(' expression-list[opt] ')' [C++ 5.2.3] -/// [C++11] typename-specifier braced-init-list [C++11 5.2.3] -/// [C++] 'const_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] -/// [C++] 'dynamic_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] -/// [C++] 'reinterpret_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] -/// [C++] 'static_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] -/// [C++] 'typeid' '(' expression ')' [C++ 5.2p1] -/// [C++] 'typeid' '(' type-id ')' [C++ 5.2p1] -/// [C++] 'this' [C++ 9.3.2] -/// [G++] unary-type-trait '(' type-id ')' -/// [G++] binary-type-trait '(' type-id ',' type-id ')' [TODO] -/// [EMBT] array-type-trait '(' type-id ',' integer ')' -/// [clang] '^' block-literal +/// [C++11] simple-type-specifier braced-init-list [C++11 5.2.3] [C++] +/// typename-specifier '(' expression-list[opt] ')' [C++ 5.2.3] [C++11] +/// typename-specifier braced-init-list [C++11 5.2.3] [C++] +/// 'const_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] [C++] +/// 'dynamic_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] [C++] +/// 'reinterpret_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] [C++] +/// 'static_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] [C++] +/// 'typeid' '(' expression ')' [C++ 5.2p1] [C++] +/// 'typeid' '(' type-id ')' [C++ 5.2p1] [C++] +/// 'this' [C++ 9.3.2] [G++] unary-type-trait '(' type-id ')' [G++] +/// binary-type-trait '(' type-id ',' type-id ')' [TODO] [EMBT] +/// array-type-trait '(' type-id ',' integer ')' [clang] '^' block-literal /// /// constant: [C99 6.4.4] /// integer-constant @@ -1317,6 +1316,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 +2542,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 +2778,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 +2792,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: Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -17045,6 +17045,7 @@ QualType ResultTy; switch (Kind) { case SourceLocExpr::File: + case SourceLocExpr::FileName: case SourceLocExpr::Function: { QualType ArrTy = Context.getStringLiteralArrayType(Context.CharTy, 0); ResultTy = Index: clang/test/Preprocessor/feature_tests.c =================================================================== --- clang/test/Preprocessor/feature_tests.c +++ 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) Index: clang/test/Preprocessor/feature_tests.cpp =================================================================== --- clang/test/Preprocessor/feature_tests.cpp +++ 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) || \ Index: clang/test/Sema/source_location.c =================================================================== --- clang/test/Sema/source_location.c +++ 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(_"), ""); Index: clang/test/SemaCXX/Inputs/source-location-file.h =================================================================== --- clang/test/SemaCXX/Inputs/source-location-file.h +++ 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 Index: clang/test/SemaCXX/source_location.cpp =================================================================== --- clang/test/SemaCXX/source_location.cpp +++ 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())); Index: clang/unittests/AST/ASTImporterTest.cpp =================================================================== --- clang/unittests/AST/ASTImporterTest.cpp +++ 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(