diff --git a/kernel/io.h b/kernel/io.h index 171f47a80..1585987b8 100644 --- a/kernel/io.h +++ b/kernel/io.h @@ -197,6 +197,28 @@ check_format(std::string_view fmt, int fmt_start, bool *has_escapes, FoundFormat ensure_no_format_spec(fmt, fmt_start, has_escapes); } +template +static auto has_name_member_imp(int) + -> decltype(static_cast(std::declval().name), std::true_type{}); + +template +static auto has_name_member_imp(long) + -> std::false_type; + +template +struct has_name_member : decltype(has_name_member_imp(0)){}; + +template +static auto ptr_has_name_member_imp(int) + -> decltype(static_cast(std::declval()->name), std::true_type{}); + +template +static auto ptr_has_name_member_imp(long) + -> std::false_type; + +template +struct ptr_has_name_member : decltype(ptr_has_name_member_imp(0)){}; + // Check that the format string `fmt.substr(fmt_start)` is valid for the given type arguments. // Fills `specs` with the FoundFormatSpecs found in the format string. // `int_args_consumed` is the number of int arguments already consumed to satisfy the @@ -245,7 +267,9 @@ constexpr void check_format(std::string_view fmt, int fmt_start, bool *has_escap if constexpr (!std::is_convertible_v && !std::is_convertible_v && !std::is_convertible_v && - !std::is_convertible_v) { + !std::is_convertible_v && + !has_name_member() && + !ptr_has_name_member()) { YOSYS_ABORT("Expected type convertible to char *"); } *specs = found; @@ -343,6 +367,16 @@ inline void format_emit_one(std::string &result, std::string_view fmt, const Fou format_emit_idstring(result, spec, dynamic_ints, num_dynamic_ints, s); return; } + if constexpr (has_name_member()) { + const std::string &s = arg.name.unescape(); + format_emit_string(result, spec, dynamic_ints, num_dynamic_ints, s); + return; + } + if constexpr (ptr_has_name_member()) { + const std::string &s = arg->name.unescape(); + format_emit_string(result, spec, dynamic_ints, num_dynamic_ints, s); + return; + } break; case CONVSPEC_VOID_PTR: if constexpr (std::is_convertible_v) {