Hi! The following WIP patch implements __builtin_source_location (), which returns const void pointer to a std::source_location::__impl struct that is required to contain __file, __function, __line and __column fields, the first two with const char * type, the latter some integral type.
I don't have testcase coverage yet and the hash map to allow sharing of VAR_DECLs with the same location is commented out both because it doesn't compile for some reason and because hashing on location_t is not enough, we probably need to hash on both location_t and fndecl, as the baz case in the following shows. Comments? namespace std { struct source_location { struct __impl { const char *__file; const char *__function; unsigned int __line, __column; }; const void *__ptr; constexpr source_location () : __ptr (nullptr) {} static consteval source_location current (const void *__p = __builtin_source_location ()) { source_location __ret; __ret.__ptr = __p; return __ret; } constexpr const char *file () const { return static_cast <const __impl *> (__ptr)->__file; } constexpr const char *function () const { return static_cast <const __impl *> (__ptr)->__function; } constexpr unsigned line () const { return static_cast <const __impl *> (__ptr)->__line; } constexpr unsigned column () const { return static_cast <const __impl *> (__ptr)->__column; } }; } using namespace std; consteval source_location bar (const source_location x = source_location::current ()) { return x; } void foo (const char **p, unsigned *q) { constexpr source_location s = source_location::current (); constexpr source_location t = bar (); p[0] = s.file (); p[1] = s.function (); q[0] = s.line (); q[1] = s.column (); p[2] = t.file (); p[3] = t.function (); q[2] = t.line (); q[3] = t.column (); constexpr const char *r = s.file (); } template <int N> constexpr source_location baz () { return source_location::current (); } constexpr source_location s1 = baz <0> (); constexpr source_location s2 = baz <1> (); const source_location *p1 = &s1; const source_location *p2 = &s2; --- gcc/cp/tree.c.jj 2019-11-13 10:54:45.437045793 +0100 +++ gcc/cp/tree.c 2019-11-14 18:11:42.391213117 +0100 @@ -445,7 +445,9 @@ builtin_valid_in_constant_expr_p (const_ if (DECL_BUILT_IN_CLASS (decl) != BUILT_IN_NORMAL) { if (fndecl_built_in_p (decl, CP_BUILT_IN_IS_CONSTANT_EVALUATED, - BUILT_IN_FRONTEND)) + BUILT_IN_FRONTEND) + || fndecl_built_in_p (decl, CP_BUILT_IN_SOURCE_LOCATION, + BUILT_IN_FRONTEND)) return true; /* Not a built-in. */ return false; --- gcc/cp/constexpr.c.jj 2019-11-13 10:54:45.426045960 +0100 +++ gcc/cp/constexpr.c 2019-11-14 18:26:40.691581038 +0100 @@ -1238,6 +1238,9 @@ cxx_eval_builtin_function_call (const co return boolean_true_node; } + if (fndecl_built_in_p (fun, CP_BUILT_IN_SOURCE_LOCATION, BUILT_IN_FRONTEND)) + return fold_builtin_source_location (EXPR_LOCATION (t)); + /* Be permissive for arguments to built-ins; __builtin_constant_p should return constant false for a non-constant argument. */ constexpr_ctx new_ctx = *ctx; --- gcc/cp/name-lookup.c.jj 2019-11-13 10:54:45.495044911 +0100 +++ gcc/cp/name-lookup.c 2019-11-14 18:38:30.765804391 +0100 @@ -5747,6 +5747,8 @@ get_std_name_hint (const char *name) {"shared_lock", "<shared_mutex>", cxx14}, {"shared_mutex", "<shared_mutex>", cxx17}, {"shared_timed_mutex", "<shared_mutex>", cxx14}, + /* <source_location>. */ + {"source_location", "<source_location>", cxx2a}, /* <sstream>. */ {"basic_stringbuf", "<sstream>", cxx98}, {"basic_istringstream", "<sstream>", cxx98}, --- gcc/cp/cp-gimplify.c.jj 2019-11-06 08:58:38.036473709 +0100 +++ gcc/cp/cp-gimplify.c 2019-11-14 20:22:32.905068438 +0100 @@ -35,6 +35,9 @@ along with GCC; see the file COPYING3. #include "attribs.h" #include "asan.h" #include "gcc-rich-location.h" +#include "output.h" +#include "file-prefix-map.h" +#include "cgraph.h" /* Forward declarations. */ @@ -896,8 +899,12 @@ cp_gimplify_expr (tree *expr_p, gimple_s tree decl = cp_get_callee_fndecl_nofold (*expr_p); if (decl && fndecl_built_in_p (decl, CP_BUILT_IN_IS_CONSTANT_EVALUATED, - BUILT_IN_FRONTEND)) + BUILT_IN_FRONTEND)) *expr_p = boolean_false_node; + else if (decl + && fndecl_built_in_p (decl, CP_BUILT_IN_SOURCE_LOCATION, + BUILT_IN_FRONTEND)) + *expr_p = fold_builtin_source_location (EXPR_LOCATION (*expr_p)); } break; @@ -2641,9 +2648,17 @@ cp_fold (tree x) /* Defer folding __builtin_is_constant_evaluated. */ if (callee && fndecl_built_in_p (callee, CP_BUILT_IN_IS_CONSTANT_EVALUATED, - BUILT_IN_FRONTEND)) + BUILT_IN_FRONTEND)) break; + if (callee + && fndecl_built_in_p (callee, CP_BUILT_IN_SOURCE_LOCATION, + BUILT_IN_FRONTEND)) + { + x = fold_builtin_source_location (EXPR_LOCATION (x)); + break; + } + x = copy_node (x); m = call_expr_nargs (x); @@ -2868,4 +2883,172 @@ process_stmt_hotness_attribute (tree std return std_attrs; } +/* Helper of fold_builtin_source_location, return the + std::source_location::__impl type after performing verification + on it. */ + +static tree +get_source_location_impl (location_t loc) +{ + tree name = get_identifier ("source_location"); + tree decl = lookup_qualified_name (std_node, name); + if (TREE_CODE (decl) != TYPE_DECL) + { + auto_diagnostic_group d; + if (decl == error_mark_node || TREE_CODE (decl) == TREE_LIST) + qualified_name_lookup_error (std_node, name, decl, loc); + else + error ("%<std::%D%> is not a type", decl); + return error_mark_node; + } + name = get_identifier ("__impl"); + tree type = TREE_TYPE (decl); + decl = lookup_qualified_name (type, name); + if (TREE_CODE (decl) != TYPE_DECL) + { + auto_diagnostic_group d; + if (decl == error_mark_node || TREE_CODE (decl) == TREE_LIST) + qualified_name_lookup_error (type, name, decl, loc); + else + error ("%<std::source_location::%D%> is not a type", decl); + return error_mark_node; + } + type = TREE_TYPE (decl); + if (TREE_CODE (type) != RECORD_TYPE) + { + error ("%<std::source_location::%D%> is not a class type", decl); + return error_mark_node; + } + + int cnt = 0; + for (tree field = TYPE_FIELDS (type); + (field = next_initializable_field (field)) != NULL_TREE; + field = DECL_CHAIN (field)) + { + if (DECL_NAME (field) != NULL_TREE) + { + const char *n = IDENTIFIER_POINTER (DECL_NAME (field)); + if (strcmp (n, "__file") == 0 || strcmp (n, "__function") == 0) + { + if (TREE_TYPE (field) != const_string_type_node) + { + error ("%<std::source_location::__impl::%D%> does not " + "have %<const char *%> type", field); + return error_mark_node; + } + cnt++; + continue; + } + else if (strcmp (n, "__line") == 0 || strcmp (n, "__column") == 0) + { + if (TREE_CODE (TREE_TYPE (field)) != INTEGER_TYPE) + { + error ("%<std::source_location::__impl::%D%> does not " + "have integral type", field); + return error_mark_node; + } + cnt++; + continue; + } + } + cnt = 0; + break; + } + if (cnt != 4) + { + error ("%<std::source_location::__impl%> does not contain only " + "non-static data members %<__file%>, %<__function%>, " + "%<__line%> and %<__column%>"); + return error_mark_node; + } + return build_qualified_type (type, TYPE_QUAL_CONST); +} + +/* static GTY(()) hash_map <location_hash, tree> *source_location_table; */ +static GTY(()) unsigned int source_location_id; + +/* Fold __builtin_source_location () call. LOC is the location + of the call. */ + +tree +fold_builtin_source_location (location_t loc) +{ + if (source_location_impl == NULL_TREE) + { + auto_diagnostic_group d; + source_location_impl = get_source_location_impl (loc); + if (source_location_impl == error_mark_node) + inform (loc, "evaluating %qs", "__builtin_source_location"); + } + if (source_location_impl == error_mark_node) + return build_zero_cst (const_ptr_type_node); +// if (source_location_table) +// hash_map <location_hash, tree>::create_ggc (64); + loc = LOCATION_LOCUS (loc); +// tree *varp = source_location_table->get (loc); + tree var; +// if (*varp) +// var = *varp; +// else + { + char tmp_name[32]; + ASM_GENERATE_INTERNAL_LABEL (tmp_name, "Lsrc_loc", source_location_id++); + var = build_decl (loc, VAR_DECL, get_identifier (tmp_name), + source_location_impl); + TREE_STATIC (var) = 1; + TREE_PUBLIC (var) = 0; + DECL_ARTIFICIAL (var) = 1; + DECL_IGNORED_P (var) = 1; + DECL_EXTERNAL (var) = 0; + DECL_DECLARED_CONSTEXPR_P (var) = 1; + DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (var) = 1; + layout_decl (var, 0); + + vec<constructor_elt, va_gc> *v = NULL; + vec_alloc (v, 4); + for (tree field = TYPE_FIELDS (source_location_impl); + (field = next_initializable_field (field)) != NULL_TREE; + field = DECL_CHAIN (field)) + { + const char *n = IDENTIFIER_POINTER (DECL_NAME (field)); + tree val = NULL_TREE; + if (strcmp (n, "__file") == 0) + { + if (const char *fname = LOCATION_FILE (loc)) + { + fname = remap_macro_filename (fname); + val = build_string_literal (strlen (fname) + 1, fname); + } + else + val = build_string_literal (1, ""); + } + else if (strcmp (n, "__function") == 0) + { + const char *name = ""; + + if (current_function_decl) + name = cxx_printable_name (current_function_decl, 0); + + val = build_string_literal (strlen (name) + 1, name); + } + else if (strcmp (n, "__line") == 0) + val = build_int_cst (TREE_TYPE (field), LOCATION_LINE (loc)); + else if (strcmp (n, "__column") == 0) + val = build_int_cst (TREE_TYPE (field), LOCATION_COLUMN (loc)); + else + gcc_unreachable (); + CONSTRUCTOR_APPEND_ELT (v, field, val); + } + + tree ctor = build_constructor (source_location_impl, v); + TREE_CONSTANT (ctor) = 1; + TREE_STATIC (ctor) = 1; + DECL_INITIAL (var) = ctor; + varpool_node::finalize_decl (var); +// source_location_table->put (loc, var); + } + + return build_fold_addr_expr_with_type_loc (loc, var, const_ptr_type_node); +} + #include "gt-cp-cp-gimplify.h" --- gcc/cp/cp-tree.h.jj 2019-11-13 10:54:45.495044911 +0100 +++ gcc/cp/cp-tree.h 2019-11-14 18:44:36.632251612 +0100 @@ -204,6 +204,8 @@ enum cp_tree_index CPTI_ANY_TARG, + CPTI_SOURCE_LOCATION_IMPL, + CPTI_MAX }; @@ -356,6 +358,9 @@ extern GTY(()) tree cp_global_trees[CPTI /* A node which matches any template argument. */ #define any_targ_node cp_global_trees[CPTI_ANY_TARG] +/* std::source_location::__impl class. */ +#define source_location_impl cp_global_trees[CPTI_SOURCE_LOCATION_IMPL] + /* Node to indicate default access. This must be distinct from the access nodes in tree.h. */ @@ -6175,6 +6180,7 @@ struct GTY((chain_next ("%h.next"))) tin enum cp_built_in_function { CP_BUILT_IN_IS_CONSTANT_EVALUATED, CP_BUILT_IN_INTEGER_PACK, + CP_BUILT_IN_SOURCE_LOCATION, CP_BUILT_IN_LAST }; @@ -7723,6 +7729,7 @@ extern void clear_fold_cache (void); extern tree lookup_hotness_attribute (tree); extern tree process_stmt_hotness_attribute (tree, location_t); extern bool simple_empty_class_p (tree, tree, tree_code); +extern tree fold_builtin_source_location (location_t); /* in name-lookup.c */ extern tree strip_using_decl (tree); --- gcc/cp/decl.c.jj 2019-11-12 09:09:33.598815768 +0100 +++ gcc/cp/decl.c 2019-11-14 18:10:35.918221482 +0100 @@ -4290,6 +4290,12 @@ cxx_init_decl_processing (void) BUILT_IN_FRONTEND, NULL, NULL_TREE); set_call_expr_flags (decl, ECF_CONST | ECF_NOTHROW | ECF_LEAF); + tree cptr_ftype = build_function_type_list (const_ptr_type_node, NULL_TREE); + decl = add_builtin_function ("__builtin_source_location", + cptr_ftype, CP_BUILT_IN_SOURCE_LOCATION, + BUILT_IN_FRONTEND, NULL, NULL_TREE); + set_call_expr_flags (decl, ECF_CONST | ECF_NOTHROW | ECF_LEAF); + integer_two_node = build_int_cst (NULL_TREE, 2); /* Guess at the initial static decls size. */ Jakub