Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

internals: add _defaultctor function for defining ctors #57317

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

vtjnash
Copy link
Member

@vtjnash vtjnash commented Feb 9, 2025

Deferring this part of defining the ctor code allows it to do some value-based optimizations, without the awkwardness previously of needing to do conditional lowering of all of the possible versions that might be useful just from syntax transforms. This feels very like a generated function: how it expands just the function body, except it is flipped so that then we wrap the result in a single Method instead of being created from a Method!

Avoids lowering inaccuracies where a variable appears but is not used. These warnings are now prevented:

julia> struct Foo{T}
           x::(T; Any)
       end
WARNING: method definition for Foo at REPL[1]:2 declares type variable T but does not use it.

julia> struct Foo{T}
   x::Union{Any, T}
end
WARNING: method definition for Foo at REPL[1]:2 declares type variable T but does not use it.

Avoids hitting #31542. This lowering mistake is now avoided:

julia> struct Foo{T}
           x::(Val{T} where T)
       end
ERROR: syntax: invalid variable expression in "where" around REPL[1]:2

As a minor optimization, this avoids generating a convert call when the user declares explicit ::Any declarations, the same as if the user didn't annotate the field type:

julia> struct Foo
           x::Any
           y::Int
           z
       end

julia> code_lowered(Foo)[2]
CodeInfo(
    @ REPL[1]:2 within `unknown scope`
1%1 =   builtin Core.fieldtype(#ctor-self#, 2)%2 = y
│        @_4 = %2%4 =   builtin @_4 isa %1
└──      goto #3 if not %4
2 ─      goto #4
3@_4 = Base.convert(%1, @_4)
4%8 = @_4%9 = %new(#ctor-self#, x, %8, z)
└──      return %9
)

The outer/inner names might be a bit historical at this point (predating where clauses allowing specifying them flexibly inside or outside of the struct def): they are really exact-type-type&convert-args-from-any / exact-arg-types&apply-type-from-args if named for precisely what they do.

Deferring this part of defining the ctor code allows it to do some
value-based optimizations, without the awkwardness previously of needing
to do conditional lowering of all of the possible versions that might be
useful just from syntax transforms. This feels very like a generated
function: how it expands just the function body, except it is flipped so
that then we wrap the result in a single Method instead of being created
from a Method!

Avoids lowering inaccuracies where a variable appears but is not used.
These warnings are now prevented:
```julia
julia> struct Foo{T}
           x::(T; Any)
       end
WARNING: method definition for Foo at REPL[1]:2 declares type variable T but does not use it.

julia> struct Foo{T}
   x::Union{Any, T}
end
WARNING: method definition for Foo at REPL[1]:2 declares type variable T but does not use it.
```

Avoids hitting #31542.
This lowering mistake is now avoided:
```julia
julia> struct Foo{T}
           x::(Val{T} where T)
       end
ERROR: syntax: invalid variable expression in "where" around REPL[1]:2
```

As a minor optimization, this avoids generating a `convert` call when
the user declares explicit `::Any` declarations, the same as if the user
didn't annotate the field type:
```julia
julia> struct Foo
           x::Any
           y::Int
           z
       end

julia> code_lowered(Foo)[2]
CodeInfo(
    @ REPL[1]:2 within `unknown scope`
1 ─ %1 =   builtin Core.fieldtype(#ctor-self#, 2)
│   %2 = y
│        @_4 = %2
│   %4 =   builtin @_4 isa %1
└──      goto #3 if not %4
2 ─      goto #4
3 ─      @_4 = Base.convert(%1, @_4)
4 ┄ %8 = @_4
│   %9 = %new(#ctor-self#, x, %8, z)
└──      return %9
)
```
@vtjnash vtjnash added compiler:lowering Syntax lowering (compiler front end, 2nd stage) backport 1.12 Change should be backported to release-1.12 labels Feb 9, 2025
@oscardssmith
Copy link
Member

Does JuliaLowering need to copy this?

@Keno
Copy link
Member

Keno commented Feb 10, 2025

Is there any strong reason to write this code in C? I have been generally hoping to move more of this kind of code to Julia.

@vtjnash
Copy link
Member Author

vtjnash commented Feb 11, 2025

Bootstrapping mainly--ctors are pretty low level and important. In theory this should be replaceable by any arbitrary call later, although Revise may want to model it

@vtjnash
Copy link
Member Author

vtjnash commented Feb 11, 2025

It may be an interesting use case for binding replacement too, as we could turn this into a julia function after bootstrapping gets far enough

@Keno
Copy link
Member

Keno commented Feb 11, 2025

I guess my question was mainly, which default ctors do we define between the point where bootstrapping is far enough that struct definition works at all and the point where we have enough infrastructure to write this function. My intuition would be that that delta isn't actually that large, but I don't know.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport 1.12 Change should be backported to release-1.12 compiler:lowering Syntax lowering (compiler front end, 2nd stage)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants