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

WIP/RFC: Sweet Holy Traits #13222

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 61 additions & 20 deletions base/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,50 @@ call(::Type{Matrix}) = Array{Any}(0, 0)
function call{P<:Ptr,T<:Ptr}(::Type{Ref{P}}, a::Array{T}) # Ref{P<:Ptr}(a::Array{T<:Ptr})
return RefArray(a) # effectively a no-op
end
function call{P<:Ptr,T}(::Type{Ref{P}}, a::Array{T}) # Ref{P<:Ptr}(a::Array)

## Replacing this if-else with traitfns:
# function call{P<:Ptr,T}(::Type{Ref{P}}, a::Array{T}) # Ref{P<:Ptr}(a::Array)
# if (!isbits(T) && T <: eltype(P))
# # this Array already has the right memory layout for the requested Ref
# return RefArray(a,1,false) # root something, so that this function is type-stable
# else
# ptrs = Array(P, length(a)+1)
# roots = Array(Any, length(a))
# for i = 1:length(a)
# root = cconvert(P, a[i])
# ptrs[i] = unsafe_convert(P, root)::P
# roots[i] = root
# end
# ptrs[length(a)+1] = C_NULL
# return RefArray(ptrs,1,roots)
# end
# end

"Make a trait custom trait (just for illustration)"
@traitdef IsJustRight{P,T}
@generated function trait{P,T}(::Type{IsJustRight{P,T}})
if (!isbits(T) && T <: eltype(P))
# this Array already has the right memory layout for the requested Ref
return RefArray(a,1,false) # root something, so that this function is type-stable
return :(IsJustRight{P,T})
else
ptrs = Array(P, length(a)+1)
roots = Array(Any, length(a))
for i = 1:length(a)
root = cconvert(P, a[i])
ptrs[i] = unsafe_convert(P, root)::P
roots[i] = root
end
ptrs[length(a)+1] = C_NULL
return RefArray(ptrs,1,roots)
return :(Not{IsJustRight{P,T}})
end
end
@traitfn function call{P<:Ptr,T; IsJustRight{P,T}}(::Type{Ref{P}}, a::Array{T}) # Ref{P<:Ptr}(a::Array)
# this Array already has the right memory layout for the requested Ref
return RefArray(a,1,false) # root something, so that this function is type-stable
end
@traitfn function call{P<:Ptr,T; !IsJustRight{P,T}}(::Type{Ref{P}}, a::Array{T}) # Ref{P<:Ptr}(a::Array)
ptrs = Array(P, length(a)+1)
roots = Array(Any, length(a))
for i = 1:length(a)
root = cconvert(P, a[i])
ptrs[i] = unsafe_convert(P, root)::P
roots[i] = root
end
ptrs[length(a)+1] = C_NULL
return RefArray(ptrs,1,roots)
end

cconvert{P<:Ptr,T<:Ptr}(::Union{Type{Ptr{P}},Type{Ref{P}}}, a::Array{T}) = a
cconvert{P<:Ptr}(::Union{Type{Ptr{P}},Type{Ref{P}}}, a::Array) = Ref{P}(a)

Expand All @@ -54,7 +82,9 @@ asize_from(a::Array, n) = n > ndims(a) ? () : (arraysize(a,n), asize_from(a, n+1
size{_,N}(a::Array{_,N}) = asize_from(a, 1)::NTuple{N,Int}

length(a::Array) = arraylen(a)
elsize{T}(a::Array{T}) = isbits(T) ? sizeof(T) : sizeof(Ptr)
#elsize{T}(a::Array{T}) = isbits(T) ? sizeof(T) : sizeof(Ptr)
@traitfn elsize{T; IsBits{T}}(a::Array{T}) = sizeof(T)
@traitfn elsize{T; !IsBits{T}}(a::Array{T}) = sizeof(Ptr)
sizeof(a::Array) = elsize(a) * length(a)

strides{T}(a::Array{T,1}) = (1,)
Expand All @@ -75,13 +105,24 @@ function unsafe_copy!{T}(dest::Ptr{T}, src::Ptr{T}, n)
return dest
end

function unsafe_copy!{T}(dest::Array{T}, doffs, src::Array{T}, soffs, n)
if isbits(T)
unsafe_copy!(pointer(dest, doffs), pointer(src, soffs), n)
else
for i=0:n-1
@inbounds arrayset(dest, src[i+soffs], i+doffs)
end
# function unsafe_copy!{T}(dest::Array{T}, doffs, src::Array{T}, soffs, n)
# if isbits(T)
# unsafe_copy!(pointer(dest, doffs), pointer(src, soffs), n)
# else
# for i=0:n-1
# @inbounds arrayset(dest, src[i+soffs], i+doffs)
# end
# end
# return dest
# end

@traitfn function unsafe_copy!{T; IsBits{T}}(dest::Array{T}, doffs, src::Array{T}, soffs, n)
unsafe_copy!(pointer(dest, doffs), pointer(src, soffs), n)
return dest
end
@traitfn function unsafe_copy!{T; !IsBits{T}}(dest::Array{T}, doffs, src::Array{T}, soffs, n)
for i=0:n-1
@inbounds arrayset(dest, src[i+soffs], i+doffs)
end
return dest
end
Expand Down
5 changes: 5 additions & 0 deletions base/coreimg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ include("int.jl")
include("operators.jl")
include("pointer.jl")

# traits
getindex(A::Array, i1::Real) = arrayref(A, to_index(i1))
include("traits.jl")
#include("traits-bootstrap-tests.jl")

# core array operations
include("abstractarray.jl")
typealias StridedArray{T,N,A<:DenseArray,I<:Tuple{Vararg{RangeIndex}}} DenseArray{T,N}
Expand Down
17 changes: 17 additions & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,23 @@ export
WString,
Zip,

# Traits and related functions
IsAnything,
IsNothing,
IsCallable,
IsBits,
Isimmutable,
IsLeafType,
IsContiguous,
HasFastLinearIndex,
Not,
Trait,
istrait,
trait,
@traitdef,
@traitimpl,
@traitfn,

# Ccall types
Cchar,
Cdouble,
Expand Down
4 changes: 4 additions & 0 deletions base/sysimg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ include("pointer.jl")
include("refpointer.jl")
include("functors.jl")

# traits
include("traits.jl")
#include("traits-bootstrap-tests.jl") # also activate the printing above

# array structures
include("abstractarray.jl")
include("subarray.jl")
Expand Down
98 changes: 98 additions & 0 deletions base/traits-bootstrap-tests.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Tests which can be run during bootstrap. Essentially the same as in test/traits.jl

println("--------------------trait-----------------")
println("Running traits tests:")
### tests
@traitdef Tr{X}
@traitimpl Tr{Int}
@traitfn ff985{X; Tr{X}}(x::X) = x
a = ff985(5)
# println("Running traitfn:")
# println(ff985(5))

## run tests

macro assert_(ex)
:($(esc(ex)) ? $(nothing) : (print("------------- Error in: "); println($(esc(ex)))) )
end

@traitdef Tr1{X}
@assert_ trait(Tr1{Int})==Not{Tr1{Int}}
@assert_ !istrait(Tr1{Int})
@traitimpl Tr1{Integer}
@assert_ trait(Tr1{Int})==Tr1{Int}
@assert_ istrait(Tr1{Int})
@assert_ trait(Tr1{Bool})==Tr1{Bool}

# Logic. trait(Tr) returns the same trait Tr if it is fulfilled and
# Not{Tr} otherwise. This is a bit confusing.
@assert_ trait(Tr1{AbstractString})==Not{Tr1{AbstractString}}
@assert_ istrait(Tr1{AbstractString})==false
@assert_ trait(Not{Tr1{AbstractString}})==Not{Tr1{AbstractString}}
@assert_ istrait(Not{Tr1{AbstractString}})==true
@assert_ trait(Not{Not{Tr1{AbstractString}}})==Not{Tr1{AbstractString}}
@assert_ istrait(Not{Not{Tr1{AbstractString}}})==false
@assert_ trait(Not{Not{Not{Tr1{AbstractString}}}})==Not{Tr1{AbstractString}}
@assert_ istrait(Not{Not{Not{Tr1{AbstractString}}}})==true

@assert_ trait(Not{Tr1{Integer}})==Tr1{Integer}
@assert_ istrait(Not{Tr1{Integer}})==false
@assert_ trait(Not{Not{Tr1{Integer}}})==Tr1{Integer}
@assert_ istrait(Not{Not{Tr1{Integer}}})==true
@assert_ trait(Not{Not{Not{Tr1{Integer}}}})==Tr1{Integer}
@assert_ istrait(Not{Not{Not{Tr1{Integer}}}})==false

@traitdef Tr2{X,Y}
@assert_ trait(Tr2{Int,AbstractFloat})==Not{Tr2{Int,AbstractFloat}}
@traitimpl Tr2{Integer, Float64}
@assert_ trait(Tr2{Int, Float64})==Tr2{Int, Float64}
@assert_ trait(Tr2{Int, Float32})==Not{Tr2{Int, Float32}}

# trait functions
@traitfn f985{X; Tr1{X}}(x::X) = 1 # def 1
@traitfn f985{X; !Tr1{X}}(x::X) = 2
@assert_ f985(5)==1
@assert_ f985(5.)==2


@traitfn f985{X,Y; Tr2{X,Y}}(x::X,y::Y,z) = 1
@assert_ f985(5,5., "a")==1
@traitfn f985{X,Y; !Tr2{X,Y}}(x::X,y::Y,z) = 2
@assert_ f985(5,5, "a")==2
# This will overwrite the definition def1 above
@traitfn f985{X; !Tr2{X,X}}(x::X) = 10
@traitfn f985{X; Tr2{X,X}}(x::X) = 100
@assert_ f985(5)==10
@assert_ f985(5.)==10
@traitimpl Tr2{Integer, Integer}
@assert_ f985(5.)==10
println("This fails on the first round of coreimg.jl:")
@assert_ f985(5)==10
println("end of failure")
# need to update method cache to make above right:
@traitfn f985{X; Tr2{X,X}}(x::X) = 100
@assert_ f985(5)==100
@assert_ f985(5.)==10

# VarArg
@traitfn g{X; Tr1{X}}(x::X, y...) = y
@assert_ g(5, 7, 8)==((7,8),)
### @assert_ g(5.0, 7, 8)==((7,8),) # hangs because of https://github.com/JuliaLang/julia/issues/13183
## With macros
@traitfn @generated ggg{X; Tr1{X}}(x::X) = ( x<:Array ? :(x[1]+1) : :(x))
#@traitfn @generated ggg{X; Tr1{X}}(x::X) = ( println(x); x<:Array ? :(x[1]+1) : :(x))
@assert_ ggg(5)==5
@traitimpl Tr1{AbstractArray}
a = Array(Any,1) # Array(Int,1) does not work yet!?
a[1] = 5
@assert_ ggg(a)==6

# traitfn with Type
@traitfn ggt{X; Tr1{X}}(::Type{X}, y) = (X,y)
@assert_ ggt(Array, 5)==(Array, 5)

# traitfn with ::X
@traitfn gg27{X; Tr1{X}}(::X) = X
@assert_ gg27(a)==Array{Int,1}

println("Traits tests done")
Loading