Skip to content

Commit 2b8773f

Browse files
committed
Merge pull request #13555 from JuliaLang/jn/pure
implement a pure function annotation
2 parents 83eac1e + b7278d1 commit 2b8773f

14 files changed

+238
-110
lines changed

base/abstractarray.jl

-9
Original file line numberDiff line numberDiff line change
@@ -117,15 +117,6 @@ linearindexing(A::AbstractArray, B::AbstractArray...) = linearindexing(linearind
117117
linearindexing(::LinearFast, ::LinearFast) = LinearFast()
118118
linearindexing(::LinearIndexing, ::LinearIndexing) = LinearSlow()
119119

120-
# The real @inline macro is not available this early in the bootstrap, so this
121-
# internal macro splices the meta Expr directly into the function body.
122-
macro _inline_meta()
123-
Expr(:meta, :inline)
124-
end
125-
macro _noinline_meta()
126-
Expr(:meta, :noinline)
127-
end
128-
129120
## Bounds checking ##
130121
@generated function trailingsize{T,N,n}(A::AbstractArray{T,N}, ::Type{Val{n}})
131122
n > N && return 1

base/essentials.jl

+7-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ end
1414
macro _noinline_meta()
1515
Expr(:meta, :noinline)
1616
end
17+
macro _pure_meta()
18+
Expr(:meta, :pure)
19+
end
1720

1821

1922
# constructors for Core types in boot.jl
@@ -74,15 +77,17 @@ macro generated(f)
7477
end
7578

7679

77-
@generated function tuple_type_head{T<:Tuple}(::Type{T})
80+
function tuple_type_head{T<:Tuple}(::Type{T})
81+
@_pure_meta
7882
T.parameters[1]
7983
end
8084

8185
isvarargtype(t::ANY) = isa(t,DataType)&&is((t::DataType).name,Vararg.name)
8286
isvatuple(t::DataType) = (n = length(t.parameters); n > 0 && isvarargtype(t.parameters[n]))
8387
unwrapva(t::ANY) = isvarargtype(t) ? t.parameters[1] : t
8488

85-
@generated function tuple_type_tail{T<:Tuple}(::Type{T})
89+
function tuple_type_tail{T<:Tuple}(::Type{T})
90+
@_pure_meta
8691
if isvatuple(T) && length(T.parameters) == 1
8792
return T
8893
end

base/expr.jl

+5-7
Original file line numberDiff line numberDiff line change
@@ -67,18 +67,16 @@ macro eval(x)
6767
end
6868

6969
macro inline(ex)
70-
esc(_inline(ex))
70+
esc(isa(ex, Expr) ? pushmeta!(ex, :inline) : ex)
7171
end
7272

73-
_inline(ex::Expr) = pushmeta!(ex, :inline)
74-
_inline(arg) = arg
75-
7673
macro noinline(ex)
77-
esc(_noinline(ex))
74+
esc(isa(ex, Expr) ? pushmeta!(ex, :noinline) : ex)
7875
end
7976

80-
_noinline(ex::Expr) = pushmeta!(ex, :noinline)
81-
_noinline(arg) = arg
77+
macro pure(ex)
78+
esc(isa(ex, Expr) ? pushmeta!(ex, :pure) : ex)
79+
end
8280

8381
## some macro utilities ##
8482

base/inference.jl

+148-50
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const MAX_TYPE_DEPTH = 5
66
const MAX_TUPLETYPE_LEN = 8
77
const MAX_TUPLE_DEPTH = 4
88

9-
type NotFound
9+
immutable NotFound
1010
end
1111

1212
const NF = NotFound()
@@ -535,7 +535,7 @@ function builtin_tfunction(f::ANY, args::ANY, argtype::ANY, vtypes::ObjectIdDict
535535
return tf[3](argtypes...)
536536
end
537537

538-
function isconstantfunc(f::ANY, sv::StaticVarInfo)
538+
function isconstantref(f::ANY, sv::StaticVarInfo)
539539
if isa(f,TopNode)
540540
m = _topmod()
541541
return isconst(m, f.name) && isdefined(m, f.name) && f
@@ -544,39 +544,46 @@ function isconstantfunc(f::ANY, sv::StaticVarInfo)
544544
M = f.mod; s = f.name
545545
return isdefined(M,s) && isconst(M,s) && f
546546
end
547-
if isa(f,Expr) && is(f.head,:call)
548-
if length(f.args) == 3 && isa(f.args[1], TopNode) &&
549-
is(f.args[1].name,:getfield) && isa(f.args[3],QuoteNode)
550-
s = f.args[3].value
551-
if isa(f.args[2],Module)
552-
M = f.args[2]
553-
else
554-
M = isconstantfunc(f.args[2], sv)
555-
if M === false
556-
return false
557-
end
558-
M = _ieval(M)
559-
if !isa(M,Module)
560-
return false
547+
if isa(f,Expr)
548+
if is(f.head,:call)
549+
if length(f.args) == 3 && isa(f.args[1], TopNode) &&
550+
is(f.args[1].name,:getfield) && isa(f.args[3],QuoteNode)
551+
s = f.args[3].value
552+
if isa(f.args[2],Module)
553+
M = f.args[2]
554+
else
555+
M = isconstantref(f.args[2], sv)
556+
if M === false
557+
return false
558+
end
559+
M = _ieval(M)
560+
if !isa(M,Module)
561+
return false
562+
end
561563
end
564+
return isdefined(M,s) && isconst(M,s) && f
562565
end
563-
return isdefined(M,s) && isconst(M,s) && f
566+
elseif is(f.head,:inert)
567+
return f
564568
end
569+
return false
565570
end
566-
567-
if isa(f,QuoteNode) && (isa(f.value, Function) || isa(f.value, IntrinsicFunction))
568-
return f.value
569-
end
570-
if isa(f,Function) || isa(f,IntrinsicFunction)
571+
if isa(f,QuoteNode)
571572
return f
572573
end
573574
if isa(f,SymbolNode)
574575
f = f.name
575576
end
576-
return isa(f,Symbol) && is_global(sv, f) && _iisconst(f) && f
577+
if isa(f,Symbol)
578+
return is_global(sv, f) && _iisconst(f) && f
579+
end
580+
if isa(f,GenSym) || isa(f,LambdaStaticData)
581+
return false
582+
end
583+
return f
577584
end
578585

579-
const isconstantref = isconstantfunc
586+
const isconstantfunc = isconstantref
580587

581588
const limit_tuple_depth = t->limit_tuple_depth_(t,0)
582589

@@ -651,25 +658,7 @@ function abstract_call_gf(f, fargs, argtype, e)
651658
end
652659
end
653660
if istopfunction(tm, f, :promote_type) || istopfunction(tm, f, :typejoin)
654-
la = length(argtypes)
655-
c = cell(la)
656-
for i = 1:la
657-
t = argtypes[i]
658-
if isType(t) && !isa(t.parameters[1],TypeVar)
659-
c[i] = t.parameters[1]
660-
else
661-
return Type
662-
end
663-
end
664-
if istopfunction(tm, f, :promote_type)
665-
try
666-
RT = Type{f(c...)}
667-
return RT
668-
catch
669-
end
670-
else
671-
return Type{f(c...)}
672-
end
661+
return Type
673662
end
674663
# don't consider more than N methods. this trades off between
675664
# compiler performance and generated code performance.
@@ -822,7 +811,109 @@ function abstract_apply(af, fargs, aargtypes::Vector{Any}, vtypes, sv, e)
822811
return abstract_call(af, (), Any[Vararg{Any}], vtypes, sv, ())
823812
end
824813

814+
function isconstantargs(args, argtypes::Vector{Any}, sv::StaticVarInfo)
815+
if isempty(argtypes)
816+
return true
817+
end
818+
if is(args,()) || isa(argtypes[end], Vararg)
819+
return false
820+
end
821+
for i = 1:length(args)
822+
arg = args[i]
823+
t = argtypes[i]
824+
if !isType(t) || has_typevars(t.parameters[1])
825+
if isconstantref(arg, sv) === false
826+
return false
827+
end
828+
end
829+
end
830+
return true
831+
end
832+
833+
function _ieval_args(args, argtypes::Vector{Any}, sv::StaticVarInfo)
834+
c = cell(length(args))
835+
for i = 1:length(argtypes)
836+
t = argtypes[i]
837+
if isType(t) && !has_typevars(t.parameters[1])
838+
c[i] = t.parameters[1]
839+
else
840+
c[i] = _ieval(isconstantref(args[i], sv))
841+
end
842+
end
843+
return c
844+
end
845+
846+
@pure function type_typeof(v::ANY)
847+
if isa(v, Type)
848+
return Type{v}
849+
end
850+
return typeof(v)
851+
end
852+
853+
function pure_eval_call(f, fargs, argtypes, sv, e)
854+
if !isa(f, Function) # TODO: maybe replace with :call?
855+
return false
856+
end
857+
if !isconstantargs(fargs, argtypes, sv)
858+
return false
859+
end
860+
861+
args = _ieval_args(fargs, argtypes, sv)
862+
tm = _topmod()
863+
if isgeneric(f)
864+
atype = Tuple{Any[type_typeof(a) for a in args]...}
865+
meth = _methods(f, atype, 1)
866+
if meth === false || length(meth) != 1
867+
return false
868+
end
869+
meth = meth[1]::SimpleVector
870+
linfo = try
871+
func_for_method(meth[3], atype, meth[2])
872+
catch
873+
NF
874+
end
875+
if linfo === NF
876+
return false
877+
end
878+
if !linfo.pure
879+
typeinf(linfo, meth[1], meth[2], linfo)
880+
if !linfo.pure
881+
return false
882+
end
883+
end
884+
elseif !isdefined(f, :code) || !f.code.pure
885+
return false
886+
end
887+
888+
local v
889+
try
890+
v = f(args...)
891+
catch
892+
return false
893+
end
894+
if isa(e, Expr) # replace Expr with a constant
895+
stmts = Any[] # check if any arguments aren't effect_free and need to be kept around
896+
for i = 1:length(fargs)
897+
arg = fargs[i]
898+
if !effect_free(arg, sv, false)
899+
push!(stmts, arg)
900+
end
901+
end
902+
if isempty(stmts)
903+
e.head = :inert # eval_annotate will turn this into a QuoteNode
904+
e.args = Any[v]
905+
else
906+
e.head = :call # should get cleaned up by tuple elimination
907+
e.args = Any[top_getfield, Expr(:call, top_tuple, stmts..., v), length(stmts) + 1]
908+
end
909+
end
910+
return type_typeof(v)
911+
end
912+
913+
825914
function abstract_call(f, fargs, argtypes::Vector{Any}, vtypes, sv::StaticVarInfo, e)
915+
t = pure_eval_call(f, fargs, argtypes, sv, e)
916+
t !== false && return t
826917
if is(f,_apply) && length(fargs)>1
827918
af = isconstantfunc(fargs[2], sv)
828919
if !is(af,false)
@@ -926,7 +1017,8 @@ function abstract_eval_call(e, vtypes, sv::StaticVarInfo)
9261017
if !(Function <: ft) && _iisdefined(:call)
9271018
call_func = _ieval(:call)
9281019
if isa(call_func,Function)
929-
return abstract_call(call_func, e.args, Any[ft,argtypes...], vtypes, sv, e)
1020+
unshift!(argtypes, ft)
1021+
return abstract_call(call_func, e.args, argtypes, vtypes, sv, e)
9301022
end
9311023
end
9321024
return Any
@@ -943,7 +1035,8 @@ end
9431035

9441036
function abstract_eval(e::ANY, vtypes, sv::StaticVarInfo)
9451037
if isa(e,QuoteNode)
946-
return typeof((e::QuoteNode).value)
1038+
v = (e::QuoteNode).value
1039+
return type_typeof(v)
9471040
elseif isa(e,TopNode)
9481041
return abstract_eval_global(_topmod(), (e::TopNode).name)
9491042
elseif isa(e,Symbol)
@@ -1019,6 +1112,9 @@ function abstract_eval(e::ANY, vtypes, sv::StaticVarInfo)
10191112
t = Function
10201113
elseif is(e.head,:copyast)
10211114
t = abstract_eval(e.args[1], vtypes, sv)
1115+
elseif is(e.head,:inert)
1116+
v = e.args[1]
1117+
return type_typeof(v)
10221118
else
10231119
t = Any
10241120
end
@@ -1713,6 +1809,9 @@ function typeinf_uncached(linfo::LambdaStaticData, atypes::ANY, sparams::SimpleV
17131809
getfield_elim_pass(fulltree.args[3], sv)
17141810
end
17151811
linfo.inferred = true
1812+
body = Expr(:block)
1813+
body.args = fulltree.args[3].args::Array{Any,1}
1814+
linfo.pure = popmeta!(body, :pure)[1]
17161815
fulltree = ccall(:jl_compress_ast, Any, (Any,Any), def, fulltree)
17171816
end
17181817

@@ -1779,6 +1878,8 @@ function eval_annotate(e::ANY, vtypes::ANY, sv::StaticVarInfo, decls, clo, undef
17791878
return e
17801879
#elseif is(head,:gotoifnot) || is(head,:return)
17811880
# e.typ = Any
1881+
elseif is(head,:inert)
1882+
return QuoteNode(e.args[1])
17821883
elseif is(head,:(=))
17831884
# e.typ = Any
17841885
s = e.args[1]
@@ -1968,10 +2069,7 @@ function exprtype(x::ANY, sv::StaticVarInfo)
19682069
return abstract_eval(x::Symbol, emptydict, sv)
19692070
elseif isa(x,QuoteNode)
19702071
v = (x::QuoteNode).value
1971-
if isa(v,Type)
1972-
return Type{v}
1973-
end
1974-
return typeof(v)
2072+
return type_typeof(v)
19752073
elseif isa(x,Type)
19762074
return Type{x}
19772075
elseif isa(x,LambdaStaticData)
@@ -2033,7 +2131,7 @@ function effect_free(e::ANY, sv, allow_volatile::Bool)
20332131
allow_volatile && return true
20342132
return isconst(e.mod, e.name)
20352133
end
2036-
if isconstantfunc(e, sv) !== false
2134+
if isconstantref(e, sv) !== false
20372135
return true
20382136
end
20392137
if isa(e,Expr)

base/iterator.jl

+2-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ immutable Zip{I, Z<:AbstractZipIterator} <: AbstractZipIterator
4545
end
4646
zip(a, b, c...) = Zip(a, zip(b, c...))
4747
length(z::Zip) = min(length(z.a), length(z.z))
48-
@generated function tuple_type_cons{S,T<:Tuple}(::Type{S}, ::Type{T})
48+
function tuple_type_cons{S,T<:Tuple}(::Type{S}, ::Type{T})
49+
@_pure_meta
4950
Tuple{S, T.parameters...}
5051
end
5152
eltype{I,Z}(::Type{Zip{I,Z}}) = tuple_type_cons(eltype(I), eltype(Z))

0 commit comments

Comments
 (0)