From 3b9965b82cb6be95fb5300021905edf8adb46428 Mon Sep 17 00:00:00 2001 From: Paulo Feodrippe Date: Sun, 23 Nov 2025 12:15:50 -0500 Subject: [PATCH 1/2] Use patched jank LLVM version https://github.com/pfeodrippe/llvm-project/commit/4b517e4cbcd1c4323b122a95c36c9c63b8721ee3 --- compiler+runtime/bin/build-clang | 2 +- compiler+runtime/src/cpp/jank/analyze/processor.cpp | 10 ---------- .../cpp/member/{skip-static.jank => pass-static.jank} | 0 3 files changed, 1 insertion(+), 11 deletions(-) rename compiler+runtime/test/jank/cpp/member/{skip-static.jank => pass-static.jank} (100%) diff --git a/compiler+runtime/bin/build-clang b/compiler+runtime/bin/build-clang index b2cb2054c..b05139bf3 100755 --- a/compiler+runtime/bin/build-clang +++ b/compiler+runtime/bin/build-clang @@ -49,7 +49,7 @@ echo "Using ${make_j} cores to build" srcdir="${PWD}" -llvm_url="https://github.com/jank-lang/llvm-project.git" +llvm_url="https://github.com/pfeodrippe/llvm-project.git" llvm_version=22 llvm_branch="jank-snapshot/llvm${llvm_version}" diff --git a/compiler+runtime/src/cpp/jank/analyze/processor.cpp b/compiler+runtime/src/cpp/jank/analyze/processor.cpp index 5ead51ee5..7a20c0f6c 100644 --- a/compiler+runtime/src/cpp/jank/analyze/processor.cpp +++ b/compiler+runtime/src/cpp/jank/analyze/processor.cpp @@ -4351,16 +4351,6 @@ namespace jank::analyze ->add_usage(read::parse::reparse_nth(l, 0)); } - if(Cpp::IsStaticDatamember(member_scope)) - { - return error::analyze_known_issue( - "A blocking Clang bug prevents access to static members in some scenarios. See " - "https://github.com/llvm/llvm-project/issues/146956 for details.", - object_source(member), - latest_expansion(macro_expansions)) - ->add_usage(read::parse::reparse_nth(l, 0)); - } - val->val_kind = expr::cpp_value::value_kind::variable; val->type = Cpp::GetLValueReferenceType(Cpp::GetTypeFromScope(member_scope)); val->scope = member_scope; diff --git a/compiler+runtime/test/jank/cpp/member/skip-static.jank b/compiler+runtime/test/jank/cpp/member/pass-static.jank similarity index 100% rename from compiler+runtime/test/jank/cpp/member/skip-static.jank rename to compiler+runtime/test/jank/cpp/member/pass-static.jank From 0909d9396af51ad0d0f47fb06c596687d1c7fa2e Mon Sep 17 00:00:00 2001 From: Paulo Feodrippe Date: Sun, 23 Nov 2025 13:42:07 -0500 Subject: [PATCH 2/2] Fix skipped inst tests, rename failed to pass-template-instantiation, fix `test/jank/cpp/constructor/builtin/pass-aliased.jank` and `test/jank/cpp/new/builtin/pass-aliased.jank` --- .../src/cpp/jank/analyze/cpp_util.cpp | 20 ++ .../src/cpp/jank/codegen/processor.cpp | 6 + .../src/cpp/jank/runtime/obj/inst.cpp | 247 ++++++++++++++++-- .../complex/fail-template-instantiation.jank | 15 -- .../complex/pass-template-instantiation.jank | 16 ++ .../reader-macro/instant/pass-fn-inst.jank | 4 + .../instant/pass-inst-full-date-time.jank | 2 + .../instant/pass-inst-no-offset.jank | 2 + .../instant/pass-inst-only-date.jank | 2 + .../instant/pass-inst-only-year-month.jank | 2 + .../instant/pass-inst-only-year.jank | 2 + .../reader-macro/instant/skip-fn-inst.jank | 3 - .../instant/skip-inst-full-date-time.jank | 3 - .../instant/skip-inst-no-offset.jank | 3 - .../instant/skip-inst-only-date.jank | 3 - 15 files changed, 279 insertions(+), 51 deletions(-) delete mode 100644 compiler+runtime/test/jank/cpp/constructor/complex/fail-template-instantiation.jank create mode 100644 compiler+runtime/test/jank/cpp/constructor/complex/pass-template-instantiation.jank create mode 100644 compiler+runtime/test/jank/reader-macro/instant/pass-fn-inst.jank create mode 100644 compiler+runtime/test/jank/reader-macro/instant/pass-inst-full-date-time.jank create mode 100644 compiler+runtime/test/jank/reader-macro/instant/pass-inst-no-offset.jank create mode 100644 compiler+runtime/test/jank/reader-macro/instant/pass-inst-only-date.jank create mode 100644 compiler+runtime/test/jank/reader-macro/instant/pass-inst-only-year-month.jank create mode 100644 compiler+runtime/test/jank/reader-macro/instant/pass-inst-only-year.jank delete mode 100644 compiler+runtime/test/jank/reader-macro/instant/skip-fn-inst.jank delete mode 100644 compiler+runtime/test/jank/reader-macro/instant/skip-inst-full-date-time.jank delete mode 100644 compiler+runtime/test/jank/reader-macro/instant/skip-inst-no-offset.jank delete mode 100644 compiler+runtime/test/jank/reader-macro/instant/skip-inst-only-date.jank diff --git a/compiler+runtime/src/cpp/jank/analyze/cpp_util.cpp b/compiler+runtime/src/cpp/jank/analyze/cpp_util.cpp index a16daf244..a4091452f 100644 --- a/compiler+runtime/src/cpp/jank/analyze/cpp_util.cpp +++ b/compiler+runtime/src/cpp/jank/analyze/cpp_util.cpp @@ -1,5 +1,7 @@ #include +#include +#include #include #include #include @@ -300,6 +302,24 @@ namespace jank::analyze::cpp_util } /* TODO: Handle typed object refs, too. */ + auto const qual_type{ clang::QualType::getFromOpaquePtr(type) }; + if(auto const *typedef_type + = llvm::dyn_cast_or_null(qual_type.getTypePtrOrNull())) + { + if(auto const *alias_decl = typedef_type->getDecl()) + { + auto alias_name{ alias_decl->getQualifiedNameAsString() }; + if(!alias_name.empty()) + { + if(Cpp::IsPointerType(type)) + { + alias_name += "*"; + } + return alias_name; + } + } + } + if(auto const scope{ Cpp::GetScopeFromType(type) }; scope) { auto name{ get_qualified_name(scope) }; diff --git a/compiler+runtime/src/cpp/jank/codegen/processor.cpp b/compiler+runtime/src/cpp/jank/codegen/processor.cpp index ea5b7b9d4..00cdffac0 100644 --- a/compiler+runtime/src/cpp/jank/codegen/processor.cpp +++ b/compiler+runtime/src/cpp/jank/codegen/processor.cpp @@ -248,6 +248,12 @@ namespace jank::codegen R"(jank::runtime::make_box("{}"))", typed_o->to_string()); } + else if constexpr(std::same_as) + { + util::format_to(buffer, + R"(jank::runtime::make_box("{}"))", + util::escape(typed_o->to_string())); + } else if constexpr(std::same_as) { util::format_to(buffer, diff --git a/compiler+runtime/src/cpp/jank/runtime/obj/inst.cpp b/compiler+runtime/src/cpp/jank/runtime/obj/inst.cpp index b7c73a8bd..c0e7416f7 100644 --- a/compiler+runtime/src/cpp/jank/runtime/obj/inst.cpp +++ b/compiler+runtime/src/cpp/jank/runtime/obj/inst.cpp @@ -1,6 +1,9 @@ +#include +#include #include #include #include +#include #include #include @@ -14,46 +17,242 @@ namespace jank::runtime::obj { } -#ifdef _LIBCPP_VERSION - /* The current version of libc++ within LLVM does not implement - * std::chrono::parse() (a C++20 feature). Until it is added or an alternative - * is used, inst parsing will be disabled - * - * To find progress for this feature in LLVM goto: - * https://github.com/llvm/llvm-project/issues/99982 - */ - static inst_time_point inst_from_string(jtl::immutable_string const &) + namespace { - throw make_box("'#inst' parsing not currently supported.").erase(); + [[noreturn]] + void throw_inst_parse_error(jtl::immutable_string const &input) + { + throw make_box(util::format("Unrecognized date/time syntax: {}", input)).erase(); + } + +#ifdef _LIBCPP_VERSION + + bool consume_if(std::string_view &remaining, char const expected) + { + if(!remaining.empty() && remaining.front() == expected) + { + remaining.remove_prefix(1); + return true; + } + return false; + } + + bool starts_with_digits(std::string_view const &remaining, size_t const digits) + { + if(remaining.size() < digits) + { + return false; + } + for(size_t i{}; i < digits; ++i) + { + if(!std::isdigit(static_cast(remaining[i]))) + { + return false; + } + } + return true; + } + + int parse_fixed_digits(std::string_view &remaining, + size_t const digits, + jtl::immutable_string const &original) + { + if(!starts_with_digits(remaining, digits)) + { + throw_inst_parse_error(original); + } + + int value{}; + for(size_t i{}; i < digits; ++i) + { + auto const c(static_cast(remaining[i])); + value = (value * 10) + (c - '0'); + } + remaining.remove_prefix(digits); + return value; + } + + int parse_fractional_millis(std::string_view &remaining, jtl::immutable_string const &original) + { + if(!consume_if(remaining, '.')) + { + return 0; + } + + int value{}; + size_t stored_digits{}; + size_t total_digits{}; + while(!remaining.empty() && std::isdigit(static_cast(remaining.front()))) + { + if(stored_digits < 9) + { + auto const digit(static_cast(remaining.front()) - '0'); + value = (value * 10) + digit; + ++stored_digits; + } + ++total_digits; + remaining.remove_prefix(1); + } + + if(total_digits == 0) + { + throw_inst_parse_error(original); + } + + if(stored_digits == 0) + { + return 0; + } + + if(stored_digits > 3) + { + for(size_t i = stored_digits; i > 3; --i) + { + value /= 10; + } + } + else + { + for(size_t i = stored_digits; i < 3; ++i) + { + value *= 10; + } + } + + return value; + } + + int parse_timezone_offset(std::string_view &remaining, jtl::immutable_string const &original) + { + if(remaining.empty()) + { + return 0; + } + + auto const indicator(remaining.front()); + if(indicator == 'Z' || indicator == 'z') + { + remaining.remove_prefix(1); + return 0; + } + + if(indicator != '+' && indicator != '-') + { + throw_inst_parse_error(original); + } + + auto const sign(indicator == '-' ? -1 : 1); + remaining.remove_prefix(1); + auto const tz_hour(parse_fixed_digits(remaining, 2, original)); + int tz_minute{}; + + if(consume_if(remaining, ':')) + { + tz_minute = parse_fixed_digits(remaining, 2, original); + } + else if(starts_with_digits(remaining, 2)) + { + tz_minute = parse_fixed_digits(remaining, 2, original); + } + + if(tz_hour > 23 || tz_minute > 59) + { + throw_inst_parse_error(original); + } + + return sign * ((tz_hour * 60) + tz_minute); + } } -#else + static inst_time_point inst_from_string(jtl::immutable_string const &s) { - static std::vector const formats{ "%FT%T%Oz", "%FT%T%z", "%FT%T", "%F" }; + using namespace std::chrono; + + std::string_view remaining{ s.c_str(), s.size() }; - inst_time_point o; - bool parsed{ false }; + auto const year(parse_fixed_digits(remaining, 4, s)); - for(auto const format : formats) + int month{ 1 }; + int day{ 1 }; + if(consume_if(remaining, '-')) { - std::istringstream is{ static_cast(s) }; - is.imbue(std::locale("en_US.utf-8")); - is >> std::chrono::parse(format, o); + month = parse_fixed_digits(remaining, 2, s); + if(consume_if(remaining, '-')) + { + day = parse_fixed_digits(remaining, 2, s); + } + } - if(!is.fail() && is.peek() == EOF) + int hour{}; + int minute{}; + int second{}; + int millisecond{}; + + if(!remaining.empty() && (remaining.front() == 'T' || remaining.front() == 't')) + { + remaining.remove_prefix(1); + hour = parse_fixed_digits(remaining, 2, s); + if(!consume_if(remaining, ':')) + { + throw_inst_parse_error(s); + } + minute = parse_fixed_digits(remaining, 2, s); + if(!consume_if(remaining, ':')) { - parsed = true; - break; + throw_inst_parse_error(s); } + second = parse_fixed_digits(remaining, 2, s); + millisecond = parse_fractional_millis(remaining, s); + } + + if(hour > 23 || minute > 59 || second > 59) + { + throw_inst_parse_error(s); } - if(!parsed) + auto const offset_minutes(parse_timezone_offset(remaining, s)); + + if(!remaining.empty()) { - throw make_box(util::format("Unrecognized date/time syntax: {}", s)).erase(); + throw_inst_parse_error(s); } - return o; + auto const ymd(std::chrono::year{ year } / std::chrono::month{ static_cast(month) } + / std::chrono::day{ static_cast(day) }); + if(!ymd.ok()) + { + throw_inst_parse_error(s); + } + + auto const day_point(std::chrono::sys_days{ ymd }); + auto sys_time_ms(std::chrono::sys_time{ + std::chrono::duration_cast(day_point.time_since_epoch()) }); + sys_time_ms += std::chrono::hours{ hour } + std::chrono::minutes{ minute } + + std::chrono::seconds{ second } + std::chrono::milliseconds{ millisecond }; + sys_time_ms -= std::chrono::minutes{ offset_minutes }; + + return inst_time_point{ std::chrono::time_point_cast(sys_time_ms) }; } +#else + static inst_time_point inst_from_string(jtl::immutable_string const &s) + { + constexpr std::array formats{ "%FT%T%Oz", "%FT%T%z", "%FT%T", "%F", "%Y-%m", "%Y" }; + + inst_time_point parsed_value; + for(auto const *format : formats) + { + std::istringstream is{ static_cast(s) }; + is.imbue(std::locale("en_US.utf-8")); + is >> std::chrono::parse(format, parsed_value); + + if(!is.fail() && is.peek() == EOF) + { + return parsed_value; + } + } + + throw_inst_parse_error(s); + } #endif inst::inst(jtl::immutable_string const &s) diff --git a/compiler+runtime/test/jank/cpp/constructor/complex/fail-template-instantiation.jank b/compiler+runtime/test/jank/cpp/constructor/complex/fail-template-instantiation.jank deleted file mode 100644 index 59b73e64a..000000000 --- a/compiler+runtime/test/jank/cpp/constructor/complex/fail-template-instantiation.jank +++ /dev/null @@ -1,15 +0,0 @@ -(cpp/raw "namespace jank::cpp::constructor::complex::fail_template_instantiation - { - struct foo - { - template - foo(T t) - : a{ t } - { } - - std::string a{}; - }; - }") -(let* [arg (cpp/int 5) - _ (cpp/jank.cpp.constructor.complex.fail_template_instantiation.foo arg)] - :success) diff --git a/compiler+runtime/test/jank/cpp/constructor/complex/pass-template-instantiation.jank b/compiler+runtime/test/jank/cpp/constructor/complex/pass-template-instantiation.jank new file mode 100644 index 000000000..1d77a7936 --- /dev/null +++ b/compiler+runtime/test/jank/cpp/constructor/complex/pass-template-instantiation.jank @@ -0,0 +1,16 @@ +(cpp/raw "namespace jank::cpp::constructor::complex::template_instantiation + { + struct foo + { + template + foo(T t) + : a(std::to_string(t)) + { } + + std::string a{}; + }; + }") +(let* [arg (cpp/int 53) + d (cpp/jank.cpp.constructor.complex.template_instantiation.foo arg)] + (when (= (cpp/.-a d) "53") + :success)) diff --git a/compiler+runtime/test/jank/reader-macro/instant/pass-fn-inst.jank b/compiler+runtime/test/jank/reader-macro/instant/pass-fn-inst.jank new file mode 100644 index 000000000..4e9a2a1e9 --- /dev/null +++ b/compiler+runtime/test/jank/reader-macro/instant/pass-fn-inst.jank @@ -0,0 +1,4 @@ +(fn [] #inst "2025-09-01T20:47:03.713-00:00") +(when (= (str ((fn [] #inst "2025-09-01T20:47:03.713-00:00"))) + "2025-09-01T20:47:03.713-00:00") + :success) \ No newline at end of file diff --git a/compiler+runtime/test/jank/reader-macro/instant/pass-inst-full-date-time.jank b/compiler+runtime/test/jank/reader-macro/instant/pass-inst-full-date-time.jank new file mode 100644 index 000000000..b427c89b3 --- /dev/null +++ b/compiler+runtime/test/jank/reader-macro/instant/pass-inst-full-date-time.jank @@ -0,0 +1,2 @@ +(when (= (str #inst "2025-09-01T20:47:03.713-00:00") "2025-09-01T20:47:03.713-00:00") + :success) \ No newline at end of file diff --git a/compiler+runtime/test/jank/reader-macro/instant/pass-inst-no-offset.jank b/compiler+runtime/test/jank/reader-macro/instant/pass-inst-no-offset.jank new file mode 100644 index 000000000..a9fd04776 --- /dev/null +++ b/compiler+runtime/test/jank/reader-macro/instant/pass-inst-no-offset.jank @@ -0,0 +1,2 @@ +(when (= (str #inst "2025-09-01T20:47:03.713") "2025-09-01T20:47:03.713-00:00") + :success) \ No newline at end of file diff --git a/compiler+runtime/test/jank/reader-macro/instant/pass-inst-only-date.jank b/compiler+runtime/test/jank/reader-macro/instant/pass-inst-only-date.jank new file mode 100644 index 000000000..f6c00ea1c --- /dev/null +++ b/compiler+runtime/test/jank/reader-macro/instant/pass-inst-only-date.jank @@ -0,0 +1,2 @@ +(when (= (str #inst "2025-09-01") "2025-09-01T00:00:00.000-00:00") + :success) \ No newline at end of file diff --git a/compiler+runtime/test/jank/reader-macro/instant/pass-inst-only-year-month.jank b/compiler+runtime/test/jank/reader-macro/instant/pass-inst-only-year-month.jank new file mode 100644 index 000000000..3d17cc6bb --- /dev/null +++ b/compiler+runtime/test/jank/reader-macro/instant/pass-inst-only-year-month.jank @@ -0,0 +1,2 @@ +(when (= (str #inst "2025-09") "2025-09-01T00:00:00.000-00:00") + :success) diff --git a/compiler+runtime/test/jank/reader-macro/instant/pass-inst-only-year.jank b/compiler+runtime/test/jank/reader-macro/instant/pass-inst-only-year.jank new file mode 100644 index 000000000..fe334ad15 --- /dev/null +++ b/compiler+runtime/test/jank/reader-macro/instant/pass-inst-only-year.jank @@ -0,0 +1,2 @@ +(when (= (str #inst "2025") "2025-01-01T00:00:00.000-00:00") + :success) diff --git a/compiler+runtime/test/jank/reader-macro/instant/skip-fn-inst.jank b/compiler+runtime/test/jank/reader-macro/instant/skip-fn-inst.jank deleted file mode 100644 index 0419f74ab..000000000 --- a/compiler+runtime/test/jank/reader-macro/instant/skip-fn-inst.jank +++ /dev/null @@ -1,3 +0,0 @@ -(fn [] #inst "2025-09-01T20:47:03.713-00:00") - -:success \ No newline at end of file diff --git a/compiler+runtime/test/jank/reader-macro/instant/skip-inst-full-date-time.jank b/compiler+runtime/test/jank/reader-macro/instant/skip-inst-full-date-time.jank deleted file mode 100644 index f7974361b..000000000 --- a/compiler+runtime/test/jank/reader-macro/instant/skip-inst-full-date-time.jank +++ /dev/null @@ -1,3 +0,0 @@ -#inst "2025-09-01T20:47:03.713-00:00" - -:success \ No newline at end of file diff --git a/compiler+runtime/test/jank/reader-macro/instant/skip-inst-no-offset.jank b/compiler+runtime/test/jank/reader-macro/instant/skip-inst-no-offset.jank deleted file mode 100644 index 0aac69990..000000000 --- a/compiler+runtime/test/jank/reader-macro/instant/skip-inst-no-offset.jank +++ /dev/null @@ -1,3 +0,0 @@ -#inst "2025-09-01T20:47:03.713" - -:success \ No newline at end of file diff --git a/compiler+runtime/test/jank/reader-macro/instant/skip-inst-only-date.jank b/compiler+runtime/test/jank/reader-macro/instant/skip-inst-only-date.jank deleted file mode 100644 index cee4de4f4..000000000 --- a/compiler+runtime/test/jank/reader-macro/instant/skip-inst-only-date.jank +++ /dev/null @@ -1,3 +0,0 @@ -#inst "2025-09-01" - -:success \ No newline at end of file