Skip to content

Commit

Permalink
implement splatting in new calls
Browse files Browse the repository at this point in the history
  • Loading branch information
JeffBezanson committed Jan 24, 2019
1 parent 39e8c96 commit 32de7f1
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 8 deletions.
11 changes: 7 additions & 4 deletions NEWS.md
Expand Up @@ -4,8 +4,8 @@ Julia v1.2 Release Notes
New language features
---------------------

* The `extrema` function now accepts a function argument in the same manner as `minimum` and
`maximum` ([#30323]).
* Argument splatting (`x...`) can now be used in calls to the `new` pseudo-function in
constructors ([#30577]).

Multi-threading changes
-----------------------
Expand All @@ -25,11 +25,14 @@ Command-line option changes
New library functions
---------------------

* `getipaddrs()` function returns all the IP addresses of the local machine ([#30349])
* `getipaddrs()` function returns all the IP addresses of the local machine ([#30349])

Standard library changes
------------------------

* The `extrema` function now accepts a function argument in the same manner as `minimum` and
`maximum` ([#30323]).

#### LinearAlgebra

* Added keyword arguments `rtol`, `atol` to `pinv` and `nullspace` ([#29998]).
Expand All @@ -44,7 +47,7 @@ Standard library changes

#### Dates

* Fixed `repr` such that it displays `DateTime` as it would be entered in Julia ([#30200]).
* Fixed `repr` such that it displays `DateTime` as it would be entered in Julia ([#30200]).

#### Miscellaneous

Expand Down
7 changes: 7 additions & 0 deletions base/essentials.jl
Expand Up @@ -280,6 +280,13 @@ convert(::Type{Tuple{Vararg{V}}}, x::Tuple{Vararg{V}}) where {V} = x
convert(T::Type{Tuple{Vararg{V}}}, x::Tuple) where {V} =
(convert(tuple_type_head(T), x[1]), convert(T, tail(x))...)

# used for splatting in `new`
convert_prefix(::Type{Tuple{}}, x::Tuple) = x
convert_prefix(::Type{<:AtLeast1}, x::Tuple{}) = x
convert_prefix(::Type{T}, x::T) where {T<:AtLeast1} = x
convert_prefix(::Type{T}, x::AtLeast1) where {T<:AtLeast1} =
(convert(tuple_type_head(T), x[1]), convert_prefix(tuple_type_tail(T), tail(x))...)

# TODO: the following definitions are equivalent (behaviorally) to the above method
# I think they may be faster / more efficient for inference,
# if we could enable them, but are they?
Expand Down
20 changes: 16 additions & 4 deletions src/julia-syntax.scm
Expand Up @@ -696,8 +696,6 @@
(call (curly ,name ,@params) ,@field-names)))))

(define (new-call Tname type-params params args field-names field-types)
(if (any vararg? args)
(error "... is not supported inside \"new\""))
(if (any kwarg? args)
(error "\"new\" does not accept keyword arguments"))
(if (length> params (length type-params))
Expand All @@ -706,8 +704,22 @@
`(outerref ,Tname)
`(curly (outerref ,Tname)
,@type-params))))
(cond ((length> args (length field-names))
`(call (top error) "new: too many arguments"))
(cond ((length> (filter (lambda (a) (not (vararg? a))) args) (length field-names))
`(call (core throw) (call (top ArgumentError)
,(string "new: too many arguments (expected " (length field-names) ")"))))
((any vararg? args)
(if (every (lambda (ty) (equal? ty '(core Any)))
field-types)
`(splatnew ,Texpr (call (core tuple) ,@args))
(let ((tn (make-ssavalue)))
`(block
(= ,tn ,Texpr)
(splatnew ,tn (call (top convert_prefix)
(curly (core Tuple)
,@(map (lambda (fld)
`(call (core fieldtype) ,tn (quote ,fld)))
field-names))
(call (core tuple) ,@args)))))))
(else
(if (equal? type-params params)
`(new ,Texpr ,@(map (lambda (fty val)
Expand Down
30 changes: 30 additions & 0 deletions test/core.jl
Expand Up @@ -6816,3 +6816,33 @@ f29828() = 2::String
g29828() = 2::Any[String][1]
@test_throws TypeError(:typeassert, String, 2) f29828()
@test_throws TypeError(:typeassert, String, 2) g29828()

# splatting in `new`
struct SplatNew{T}
x::Int8
y::T
SplatNew{T}(args...) where {T} = new(0,args...,1)
SplatNew(args...) = new{Float32}(args...)
SplatNew{Any}(args...) = new(args...)
SplatNew{Tuple{Int16}}(args...) = new([2]..., args...)
SplatNew{Int8}() = new(1,2,3)
end
let x = SplatNew{Int16}()
@test x.x === Int8(0)
@test x.y === Int16(1)
end
@test_throws ArgumentError SplatNew{Int16}(1)
let x = SplatNew(3,2)
@test x.x === Int8(3)
@test x.y === 2.0f0
end
@test_throws ArgumentError SplatNew(1,2,3)
let x = SplatNew{Any}(1)
@test x.x === Int8(1)
@test !isdefined(x, :y)
end
let x = SplatNew{Tuple{Int16}}((1,))
@test x.x === Int8(2)
@test x.y === (Int16(1),)
end
@test_throws ArgumentError SplatNew{Int8}()

0 comments on commit 32de7f1

Please sign in to comment.