From 4722ed2e125e9a73aa4c308493e97f465e1b321a Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 28 Mar 2025 18:55:07 +0000 Subject: [PATCH] fix generated_body_to_codeinfo to avoid string (which is not always defined) Fixes a discrepancy between the code in C before #57230 and in Julia afterwards, making sure to sequence these definitions correctly. Not sure how to write a reliable test since it is specific to when this generated function is defined relative to the helpers used by this thunk, but the issue/fix is visible with: ``` $ ./julia -e 'code_lowered(ntuple, (Returns{Nothing}, Val{1000000}))' ``` --- base/Base_compiler.jl | 44 ++++++++++++++++++++++++------------------- base/essentials.jl | 2 ++ base/expr.jl | 3 ++- base/pointer.jl | 2 -- test/staged.jl | 2 ++ 5 files changed, 31 insertions(+), 22 deletions(-) diff --git a/base/Base_compiler.jl b/base/Base_compiler.jl index d4a106564a076..e22a7d980f06c 100644 --- a/base/Base_compiler.jl +++ b/base/Base_compiler.jl @@ -157,6 +157,31 @@ if false println(io::IO, x...) = Core.println(io, x...) end +## Load essential files and libraries +include("essentials.jl") + +# Because lowering inserts direct references, it is mandatory for this binding +# to exist before we start inferring code. +function string end +import Core: String + +# For OS specific stuff +# We need to strcat things here, before strings are really defined +function strcat(x::String, y::String) + out = ccall(:jl_alloc_string, Ref{String}, (Int,), Core.sizeof(x) + Core.sizeof(y)) + gc_x = @_gc_preserve_begin(x) + gc_y = @_gc_preserve_begin(y) + gc_out = @_gc_preserve_begin(out) + out_ptr = unsafe_convert(Ptr{UInt8}, out) + unsafe_copyto!(out_ptr, unsafe_convert(Ptr{UInt8}, x), Core.sizeof(x)) + unsafe_copyto!(out_ptr + Core.sizeof(x), unsafe_convert(Ptr{UInt8}, y), Core.sizeof(y)) + @_gc_preserve_end(gc_x) + @_gc_preserve_end(gc_y) + @_gc_preserve_end(gc_out) + return out +end + + """ time_ns()::UInt64 @@ -171,8 +196,6 @@ const _DOCS_ALIASING_WARNING = """ Behavior can be unexpected when any mutated argument shares memory with any other argument. """ -## Load essential files and libraries -include("essentials.jl") include("ctypes.jl") include("gcutils.jl") include("generator.jl") @@ -283,7 +306,6 @@ include("rounding.jl") include("float.jl") # Lazy strings -import Core: String include("strings/lazy.jl") function cld end @@ -320,22 +342,6 @@ using .Order include("coreir.jl") include("invalidation.jl") -# Because lowering inserts direct references, it is mandatory for this binding -# to exist before we start inferring code. -function string end - -# For OS specific stuff -# We need to strcat things here, before strings are really defined -function strcat(x::String, y::String) - out = ccall(:jl_alloc_string, Ref{String}, (Csize_t,), Core.sizeof(x) + Core.sizeof(y)) - GC.@preserve x y out begin - out_ptr = unsafe_convert(Ptr{UInt8}, out) - unsafe_copyto!(out_ptr, unsafe_convert(Ptr{UInt8}, x), Core.sizeof(x)) - unsafe_copyto!(out_ptr + Core.sizeof(x), unsafe_convert(Ptr{UInt8}, y), Core.sizeof(y)) - end - return out -end - BUILDROOT::String = "" DATAROOT::String = "" const DL_LOAD_PATH = String[] diff --git a/base/essentials.jl b/base/essentials.jl index 029205666b2bc..9f70a90bdac7d 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -723,6 +723,8 @@ cconvert(::Type{<:Ptr}, x) = x # but defer the conversion to Ptr to unsafe_conve unsafe_convert(::Type{T}, x::T) where {T} = x # unsafe_convert (like convert) defaults to assuming the convert occurred unsafe_convert(::Type{T}, x::T) where {T<:Ptr} = x # to resolve ambiguity with the next method unsafe_convert(::Type{P}, x::Ptr) where {P<:Ptr} = convert(P, x) +unsafe_convert(::Type{Ptr{UInt8}}, s::String) = ccall(:jl_string_ptr, Ptr{UInt8}, (Any,), s) +unsafe_convert(::Type{Ptr{Int8}}, s::String) = ccall(:jl_string_ptr, Ptr{Int8}, (Any,), s) """ reinterpret(::Type{Out}, x::In) diff --git a/base/expr.jl b/base/expr.jl index 634e2ddb7b4de..91b37af17c231 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -1672,7 +1672,8 @@ function generated_body_to_codeinfo(ex::Expr, defmod::Module, isva::Bool) ci = ccall(:jl_expand, Any, (Any, Any), ex, defmod) if !isa(ci, CodeInfo) if isa(ci, Expr) && ci.head === :error - error("syntax: $(ci.args[1])") + msg = ci.args[1] + error(msg isa String ? strcat("syntax: ", msg) : msg) end error("The function body AST defined by this @generated function is not pure. This likely means it contains a closure, a comprehension or a generator.") end diff --git a/base/pointer.jl b/base/pointer.jl index fdbcc7bb427b9..72c567eaf2a85 100644 --- a/base/pointer.jl +++ b/base/pointer.jl @@ -59,8 +59,6 @@ cconvert(::Type{Ptr{UInt8}}, s::AbstractString) = String(s) cconvert(::Type{Ptr{Int8}}, s::AbstractString) = String(s) unsafe_convert(::Type{Ptr{UInt8}}, x::Symbol) = ccall(:jl_symbol_name, Ptr{UInt8}, (Any,), x) unsafe_convert(::Type{Ptr{Int8}}, x::Symbol) = ccall(:jl_symbol_name, Ptr{Int8}, (Any,), x) -unsafe_convert(::Type{Ptr{UInt8}}, s::String) = ccall(:jl_string_ptr, Ptr{UInt8}, (Any,), s) -unsafe_convert(::Type{Ptr{Int8}}, s::String) = ccall(:jl_string_ptr, Ptr{Int8}, (Any,), s) cconvert(::Type{<:Ptr}, a::Array) = getfield(a, :ref) unsafe_convert(::Type{Ptr{S}}, a::AbstractArray{T}) where {S,T} = convert(Ptr{S}, unsafe_convert(Ptr{T}, a)) diff --git a/test/staged.jl b/test/staged.jl index d416b0f9a22f0..4fb5d03331711 100644 --- a/test/staged.jl +++ b/test/staged.jl @@ -477,3 +477,5 @@ module GeneratedScope57417 end @test g() == 1 end + +@test_throws "syntax: expression too large" code_lowered(ntuple, (Returns{Nothing}, Val{1000000}))