diff --git a/base/broadcast.jl b/base/broadcast.jl index afa4285f5609d..21ea426fd13f7 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -859,4 +859,229 @@ macro __dot__(x) esc(__dot__(x)) end +############################################################ +## The parser turns dotted calls into the equivalent Fusion expression. +## Effectively, this turns the Expr tree into a runtime AST, +## for a limited subset of expression types. +# +## For example, in the expression: +# d = sin.((a .+ (b .* c))...) +## The kernel becomes +# d' = Fusion{3}( +# FusionApply( +# sin, +# ( FusionCall( +# +, +# ( FusionArg{1}(), +# FusionCall( +# *, +# ( FusionArg{2}(), +# FusionArg{3}() )), )), )), +# (:a, :b, :c)) +## and then the final expansion becomes: +# d = broadcast(d', a, b, c) + +struct Fusion{N, vararg#=::Bool=#, T} + f::T + # Debugging Metadata: + # names::NTuple{N, Symbol} + # source::LineNumberNode + function Fusion{N, vararg}(f) where {N, vararg} + return new{N, vararg::Bool, typeof(f)}(f) + end +end + +struct FusionArg{N} +end + +struct FusionConstant{T} + c::T + function FusionConstant(c) where {} + return new{typeof(c)}(c) + end +end + +struct FusionCall{F, Args<:Tuple} + f::F + args::Args + function FusionCall(f, args::Tuple) where {} + return new{typeof(f), typeof(args)}(f, args) + end +end + +struct FusionApply{N, F, Args<:NTuple{N, Any}} + f::F + args::Args + function FusionApply(f, args::NTuple{N, Any}) where {N} + return new{N, typeof(f), typeof(args)}(f, args) + end +end + +function kw_to_vec(kws::Vector{Any}) + kwargs = Vector{Any}(2 * length(kws)) + for i in 1:2:length(kws) + kw = kws[i]::Tuple{Any, Any} + kwargs[i] = getfield(kw, 1) + kwargs[i + 1] = getfield(kw, 2) + end + return kwargs +end + +struct FusionKWCall{F, Args<:Tuple} + f::F + args::Args + kwargs::Vector{Any} + function FusionKWCall(f, args::Tuple; kwargs...) where {} + return new{typeof(f), typeof(args)}(f, args, kw_to_vec(kwargs)) + end +end + +struct FusionKWApply{F, Args<:Tuple} + f::F + args::Args + kwargs::Vector{Any} + function FusionKWApply(f, args::Tuple; kwargs...) where {} + return new{typeof(f), typeof(args)}(f, args, kw_to_vec(kwargs)) + end +end + +function tuplehead(t::Tuple, N::Val) + return ntuple(i -> t[i], N) +end +@generated function tupletail(t::NTuple{M, Any}, ::Val{N}) where {N, M} + # alternative, non-generated versions, + # enable when inference is improved: + #tupletail(t, Nreq) = ntuple(i -> t[i + Nreq], length(t) - Nreq) + #tupletail(t, Nreq) = t[(Nreq + 1):end] + args = Any[ :(getfield(t, $i)) for i in (N + 1):M ] + tpl = Expr(:tuple) + tpl.args = args + return tpl +end + +@inline (f::Fusion{N, false})(args::Vararg{Any, N}) where {N} = f.f(args...) +function (f::Fusion{Nreq, true})(args::Vararg{Any, M}) where {Nreq, M} + M >= Nreq || throw(MethodError(f, args)) + fargs = tuplehead(args, Val(Nreq)) + vararg = tupletail(args, Val(Nreq)) + return f.f((fargs..., vararg)...) +end +@inline (f::FusionArg{N})(args...) where {N} = args[N] +@inline (f::FusionConstant)(args...) = f.c +@inline (f::FusionCall)(args...) = f.f(map(a -> a(args...), f.args)...) +# TODO: calling _apply on map _apply is not handled by inference +# for now, we unroll some cases and generate others, to help it out +#@inline (f::FusionApply)(args...) = Core._apply(f.f, map(a -> a(args...), f.args)...) +@inline (f::FusionApply{0})(args...) = f.f() +@inline (f::FusionApply{1})(args...) = f.f(f.args[1](args...)...) +@inline (f::FusionApply{2})(args...) = f.f(f.args[1](args...)..., f.args[2](args...)...) +@inline (f::FusionApply{3})(args...) = f.f(f.args[1](args...)..., f.args[2](args...)..., f.args[3](args...)...) +@generated function (f::FusionApply{N})(args...) where {N} + fargs = Any[ :(getfield(f.args, $i)(args...)) for i in 1:N ] + return Expr(:call, GlobalRef(Core, :_apply), :(f.f), fargs...) +end +@inline function (f::FusionKWCall)(args...) + fargs = map(a -> a(args...), f.args) + # return f.f(args...; kwargs...) + if isempty(f.kwargs) + return f.f(fargs...) + else + return Core.kwfunc(f.f)(f.kwargs, f.f, fargs...) + end +end +@inline function (f::FusionKWApply)(args...) + fargs = map(a -> a(args...), f.args) + # return Core._apply(f.f, args...; kwargs...) + if isempty(f.kwargs) + return Core._apply(f.f, fargs...) + else + return Core._apply(Core.kwfunc(f.f), (f.kwargs,), (f.f,), fargs...) + end +end + +function Base.show(io::IO, f::Fusion{N, vararg}) where {N, vararg} + nargs = (vararg ? N + 1 : N) + names = String[ "a_$i" for i in 1:nargs ] # f.names + print(io, "(") + join(io, names, ", ") + vararg && print(io, "...") + print(io, ") -> ") + show_fusion(io, f.f, names) +end + +function show_fusion(io::IO, f::FusionArg{N}, names) where N + print(io, names[N]) + nothing +end + +function show_fusion(io::IO, f::FusionConstant{N}, names) where N + print(io, f.c) + nothing +end + +function show_fusion(io::IO, f::FusionCall, names) + Base.show(io, f.f) + print(io, '(') + first = true + for i in f.args + first || print(io, ", ") + first = false + show_fusion(io, i, names) + end + print(io, ')') + nothing +end + +function show_fusion(io::IO, f::FusionApply, names) + print(io, "Core._apply(") + Base.show(io, f.f) + for i in f.args + print(io, ", ") + show_fusion(io, i, names) + end + print(io, ')') + nothing +end + +function show_fusion(io::IO, f::FusionKWCall, names) + Base.show(io, f.f) + print(io, '(') + first = true + for i in f.args + first || print(io, ", ") + first = false + show_fusion(io, i, names) + end + print(io, "; ") + first = true + for i in 1:2:length(f.kwargs) + first || print(io, ", ") + first = false + print(io, f.kwargs[i]) + print(io, "=") + end + print(io, ')') + nothing +end + + +function show_fusion(io::IO, f::FusionKWApply, names) + print(io, "Core._apply(") + Base.show(io, f.f) + for i in f.args + print(io, ", ") + show_fusion(io, i, names) + end + print(io, "; #=kwargs=#...)") + nothing +end + + +function show_fusion(io::IO, @nospecialize(f), names) + print(io, "#= unexpected expression ") + show(io, f) + print(io, " =#") + nothing +end + end # module diff --git a/base/inference.jl b/base/inference.jl index f81292295d574..c6e72f2a74d3c 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -4,7 +4,7 @@ import Core: _apply, svec, apply_type, Builtin, IntrinsicFunction, MethodInstanc #### parameters limiting potentially-infinite types #### const MAX_TYPEUNION_LEN = 3 -const MAX_TYPE_DEPTH = 8 +const MAX_TYPE_DEPTH = 10 const TUPLE_COMPLEXITY_LIMIT_DEPTH = 3 const MAX_INLINE_CONST_SIZE = 256 diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index e2e940e362e6c..b8b2d5650488a 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1099,12 +1099,10 @@ (syntax-deprecation #f (string "parametric method syntax " (deparse (cadr e)) (linenode-string (function-body-lineno body))) - (deparse `(where (call ,(or name - (cadr (cadr (cadr e)))) - ,@(if (has-parameters? argl) - (cons (car argl) (cddr argl)) - (cdr argl))) - ,@raw-typevars)))) + (deparse `(where (call ,name ,@(if (has-parameters? argl) + (cons (car argl) (cddr argl)) + (cdr argl))) + ,@raw-typevars)))) (expand-forms (method-def-expr name sparams argl body rett)))) (else @@ -1687,129 +1685,108 @@ ; fuse nested calls to expr == f.(args...) into a single broadcast call, ; or a broadcast! call if lhs is non-null. (define (expand-fuse-broadcast lhs rhs) - (define (fuse? e) (and (pair? e) (eq? (car e) 'fuse))) - (define (anyfuse? exprs) - (if (null? exprs) #f (if (fuse? (car exprs)) #t (anyfuse? (cdr exprs))))) - (define (to-lambda f args kwargs) ; convert f to anonymous function with hygienic tuple args - (define (genarg arg) (if (vararg? arg) (list '... (gensy)) (gensy))) - ; (To do: optimize the case where f is already an anonymous function, in which - ; case we only need to hygienicize the arguments? But it is quite tricky - ; to fully handle splatted args, typed args, keywords, etcetera. And probably - ; the extra function call is harmless because it will get inlined anyway.) - (let ((genargs (map genarg args))) ; hygienic formal parameters - (if (null? kwargs) - `(-> ,(cons 'tuple genargs) (call ,f ,@genargs)) ; no keyword args - `(-> ,(cons 'tuple genargs) (call ,f (parameters ,@kwargs) ,@genargs))))) - (define (from-lambda f) ; convert (-> (tuple args...) (call func args...)) back to func - (if (and (pair? f) (eq? (car f) '->) (pair? (cadr f)) (eq? (caadr f) 'tuple) - (pair? (caddr f)) (eq? (caaddr f) 'call) (equal? (cdadr f) (cdr (cdaddr f)))) - (car (cdaddr f)) - f)) - (define (fuse-args oldargs) ; replace (fuse f args) with args in oldargs list - (define (fargs newargs oldargs) - (if (null? oldargs) - newargs - (fargs (if (fuse? (car oldargs)) - (append (reverse (caddar oldargs)) newargs) - (cons (car oldargs) newargs)) - (cdr oldargs)))) - (reverse (fargs '() oldargs))) - (define (fuse-funcs f args) ; for (fuse g a) in args, merge/inline g into f - ; any argument A of f that is (fuse g a) gets replaced by let A=(body of g): - (define (fuse-lets fargs args lets) - (if (null? args) - lets - (if (fuse? (car args)) - (fuse-lets (cdr fargs) (cdr args) (cons (list '= (car fargs) (caddr (cadar args))) lets)) - (fuse-lets (cdr fargs) (cdr args) lets)))) - (let ((fargs (cdadr f)) - (fbody (caddr f))) - `(-> - (tuple ,@(fuse-args (map (lambda (oldarg arg) (if (fuse? arg) - `(fuse _ ,(cdadr (cadr arg))) - oldarg)) - fargs args))) - (let (block ,@(reverse (fuse-lets fargs args '()))) ,fbody)))) - (define (dot-to-fuse e) ; convert e == (. f (tuple args)) to (fuse f args) - (define (make-fuse f args) ; check for nested (fuse f args) exprs and combine - (define (split-kwargs args) ; return (cons keyword-args positional-args) extracted from args - (define (sk args kwargs pargs) - (if (null? args) - (cons kwargs pargs) - (if (kwarg? (car args)) - (sk (cdr args) (cons (car args) kwargs) pargs) - (sk (cdr args) kwargs (cons (car args) pargs))))) - (if (has-parameters? args) - (sk (reverse (cdr args)) (cdar args) '()) - (sk (reverse args) '() '()))) - (let* ((kws.args (split-kwargs args)) - (kws (car kws.args)) - (args (cdr kws.args)) ; fusing occurs on positional args only - (args_ (map dot-to-fuse args))) - (if (anyfuse? args_) - `(fuse ,(fuse-funcs (to-lambda f args kws) args_) ,(fuse-args args_)) - `(fuse ,(to-lambda f args kws) ,args_)))) + (define nparams 0) + (define arglist '()) + (define vararg #f) + (define (dot-to-fuse e) ; convert e == (. f (tuple args)) to Fusion (if (and (pair? e) (eq? (car e) '|.|)) - (let ((f (cadr e)) (x (caddr e))) + (let ((f (cadr e)) + (x (caddr e))) (cond ((or (eq? (car x) 'quote) (eq? (car x) 'inert) (eq? (car x) '$)) `(call (core getfield) ,f ,x)) ((eq? (car x) 'tuple) - (make-fuse f (cdr x))) + (make-fusion f (cdr x))) (else (error (string "invalid syntax " (deparse e)))))) (if (and (pair? e) (eq? (car e) 'call) (dotop? (cadr e))) - (make-fuse (undotop (cadr e)) (cddr e)) + (make-fusion (undotop (cadr e)) (cddr e)) e))) - ; given e == (fuse lambda args), compress the argument list by removing (pure) - ; duplicates in args, inlining literals, and moving any varargs to the end: - (define (compress-fuse e) - (define (findfarg arg args fargs) ; for arg in args, return corresponding farg - (if (eq? arg (car args)) - (car fargs) - (findfarg arg (cdr args) (cdr fargs)))) - (if (fuse? e) + (define (will-fuse e) + (or (and (pair? e) (eq? (car e) '|.|) (eq? (car (caddr e)) 'tuple)) + (and (pair? e) (eq? (car e) 'call) (dotop? (cadr e))) + (kwarg? e) + (julia-scalar? e))) + (define (make-fusion f args) + (if (or (has-parameters? args) (any will-fuse args)) + (begin + (define fuse (make-fuse-call f args)) + (if vararg (begin + (define argn (+ nparams 1)) + (set! arglist (cons (cons `(... ,vararg) argn) arglist)) + (set! fuse (insert-vararg fuse argn)))) + (define fusion_type `(curly (|.| (top Broadcast) 'Fusion) ,nparams ,(if vararg 'true 'false))) + (define fusion `(call ,fusion_type ,fuse)) + `(fuse ,fusion ,(reverse! (map car arglist)))) + `(fuse ,f ,args))) + (define (insert-vararg form argn) + (cond ((vararg? form) + `(call (curly (|.| (top Broadcast) 'FusionArg) ,argn))) + ((and (pair? form) (contains vararg? (cdr form))) + (cons (car form) (map (lambda (x) (insert-vararg x argn)) (cdr form)))) + (else + form))) + (define (make-fuse-arg e) + (cond ((vararg? e) ; TODO: this is sloppy at order-of-execution handling + (if (and vararg (not (eq? vararg (cadr e)))) (error "multiple splatted args cannot be fused into a single broadcast")) + (set! vararg (cadr e)) + `(...)) ; return a placeholder token + ((julia-scalar? e) + `(call (|.| (top Broadcast) 'FusionConstant) ,e)) + (else + (let ((argn (memq e arglist))) + (if (not argn) (begin + (set! nparams (+ nparams 1)) + (set! argn nparams) + (set! arglist (cons (cons e argn) arglist)))) + `(call (curly (|.| (top Broadcast) 'FusionArg) ,argn)))))) + (define (make-fuse-call f args) + (define (split-kwargs args) ; return (cons keyword-args positional-args) extracted from args + (define (sk args kwargs pargs) + (if (null? args) + (cons kwargs pargs) + (if (kwarg? (car args)) + (sk (cdr args) (cons (car args) kwargs) pargs) + (sk (cdr args) kwargs (cons (car args) pargs))))) + (if (has-parameters? args) + (sk (reverse (cdr args)) (cdar args) '()) + (sk (reverse args) '() '()))) + (let* ((kws+args (split-kwargs args)) + (kws (car kws+args)) + (args (cdr kws+args)) + (args (map fuse-arg args)) ; fusing occurs on positional args only + (apply-call (any vararg? args)) + (call-head (if apply-call + (if (null? kws) 'FusionApply 'FusionKWApply) + (if (null? kws) 'FusionCall 'FusionKWCall))) + (args (if apply-call ; if calling apply, need to wrap each non-vararg args in a 1-tuple + (map (lambda (x) (if (vararg? x) + x + `(call (|.| (top Broadcast) 'FusionCall) (core tuple) (call (core tuple) ,x)))) + args) + args)) + (kws (if (null? kws) kws (list (cons 'parameters kws))))) + `(call (|.| (top Broadcast) ',call-head) ,@kws ,f (call (core tuple) ,@args)))) + (define (fuse-arg e) ; convert e into an argument for Fusion + ; examine syntax for `(. (tuple f) ...)` or `(call .op ...)` + ; or just `(. m 'field) and call appropriate lowering + (if (and (pair? e) (eq? (car e) '|.|)) (let ((f (cadr e)) - (args (caddr e))) - (define (cf old-fargs old-args new-fargs new-args renames varfarg vararg) - (if (null? old-args) - (let ((nfargs (if (null? varfarg) new-fargs (cons varfarg new-fargs))) - (nargs (if (null? vararg) new-args (cons vararg new-args)))) - `(fuse (-> (tuple ,@(reverse nfargs)) ,(replace-vars (caddr f) renames)) - ,(reverse nargs))) - (let ((farg (car old-fargs)) (arg (car old-args))) - (cond - ((and (vararg? farg) (vararg? arg)) ; arg... must be the last argument - (if (null? varfarg) - (cf (cdr old-fargs) (cdr old-args) - new-fargs new-args renames farg arg) - (if (eq? (cadr vararg) (cadr arg)) - (cf (cdr old-fargs) (cdr old-args) - new-fargs new-args (cons (cons (cadr farg) (cadr varfarg)) renames) - varfarg vararg) - (error "multiple splatted args cannot be fused into a single broadcast")))) - ((julia-scalar? arg) ; inline numeric literals etc. - (cf (cdr old-fargs) (cdr old-args) - new-fargs new-args - (cons (cons farg arg) renames) - varfarg vararg)) - ((and (symbol? arg) (memq arg new-args)) ; combine duplicate args - ; (note: calling memq for every arg is O(length(args)^2) ... - ; ... would be better to replace with a hash table if args is long) - (cf (cdr old-fargs) (cdr old-args) - new-fargs new-args - (cons (cons farg (findfarg arg new-args new-fargs)) renames) - varfarg vararg)) - (else - (cf (cdr old-fargs) (cdr old-args) - (cons farg new-fargs) (cons arg new-args) renames varfarg vararg)))))) - (cf (cdadr f) args '() '() '() '() '())) - e)) ; (not (fuse? e)) - (let ((e (compress-fuse (dot-to-fuse rhs))) ; an expression '(fuse func args) if expr is a dot call - (lhs-view (ref-to-view lhs))) ; x[...] expressions on lhs turn in to view(x, ...) to update x in-place - (if (fuse? e) + (x (caddr e))) + (cond ((or (eq? (car x) 'quote) (eq? (car x) 'inert) (eq? (car x) '$)) + (make-fuse-arg `(call (core getfield) ,f ,x))) + ((eq? (car x) 'tuple) + (make-fuse-call f (cdr x))) + (else + (error (string "invalid syntax " (deparse e)))))) + (if (and (pair? e) (eq? (car e) 'call) (dotop? (cadr e))) + (make-fuse-call (undotop (cadr e)) (cddr e)) + (make-fuse-arg e)))) + (let* ((lhs-view (ref-to-view lhs)) ; x[...] expressions on lhs turn in to view(x, ...) to update x in-place + (e (dot-to-fuse rhs))) + (if (and (pair? e) (eq? (car e) 'fuse)) (if (null? lhs) - (expand-forms `(call (top broadcast) ,(from-lambda (cadr e)) ,@(caddr e))) - (expand-forms `(call (top broadcast!) ,(from-lambda (cadr e)) ,lhs-view ,@(caddr e)))) + (expand-forms `(call (top broadcast) ,(cadr e) ,@(caddr e))) + (expand-forms `(call (top broadcast!) ,(cadr e) ,lhs-view ,@(caddr e)))) (if (null? lhs) (expand-forms e) (expand-forms `(call (top broadcast!) (top identity) ,lhs-view ,e)))))) diff --git a/test/broadcast.jl b/test/broadcast.jl index 849d44be7f694..976bbe7a8d9fa 100644 --- a/test/broadcast.jl +++ b/test/broadcast.jl @@ -315,6 +315,25 @@ let identity = error, x = [1,2,3] @test x == [1,1,1] end +# make sure scalars are inlined as FusionConstants (generated by using Core.println) +#@test expand(Main, :(f.(x, y))) == Expr(:call, GlobalRef(Base, :broadcast), Expr(:call, Expr(:call, GlobalRef(Core, :apply_type), Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:Fusion)), 2, false), Expr(:call, Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:FusionCall)), :f, Expr(:call, GlobalRef(Core, :tuple), Expr(:call, Expr(:call, GlobalRef(Core, :apply_type), Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:FusionArg)), 1)), Expr(:call, Expr(:call, GlobalRef(Core, :apply_type), Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:FusionArg)), 2))))), :x, :y) +@test expand(Main, :(f.(x, y))) == Expr(:call, GlobalRef(Base, :broadcast), :f, :x, :y) + +@test expand(Main, :(f.(x, 1))) == Expr(:call, GlobalRef(Base, :broadcast), Expr(:call, Expr(:call, GlobalRef(Core, :apply_type), Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:Fusion)), 1, false), Expr(:call, Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:FusionCall)), :f, Expr(:call, GlobalRef(Core, :tuple), Expr(:call, Expr(:call, GlobalRef(Core, :apply_type), Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:FusionArg)), 1)), Expr(:call, Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:FusionConstant)), 1)))), :x) + +@test expand(Main, :(f.(x, 1.0))) == Expr(:call, GlobalRef(Base, :broadcast), Expr(:call, Expr(:call, GlobalRef(Core, :apply_type), Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:Fusion)), 1, false), Expr(:call, Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:FusionCall)), :f, Expr(:call, GlobalRef(Core, :tuple), Expr(:call, Expr(:call, GlobalRef(Core, :apply_type), Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:FusionArg)), 1)), Expr(:call, Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:FusionConstant)), 1.0)))), :x) + +@test expand(Main, :(f.(x, $π))) == Expr(:call, GlobalRef(Base, :broadcast), Expr(:call, Expr(:call, GlobalRef(Core, :apply_type), Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:Fusion)), 1, false), Expr(:call, Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:FusionCall)), :f, Expr(:call, GlobalRef(Core, :tuple), Expr(:call, Expr(:call, GlobalRef(Core, :apply_type), Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:FusionArg)), 1)), Expr(:call, Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:FusionConstant)), π)))), :x) + +@test expand(Main, :(f.(x, "hello"))) == Expr(:call, GlobalRef(Base, :broadcast), Expr(:call, Expr(:call, GlobalRef(Core, :apply_type), Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:Fusion)), 1, false), Expr(:call, Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:FusionCall)), :f, Expr(:call, GlobalRef(Core, :tuple), Expr(:call, Expr(:call, GlobalRef(Core, :apply_type), Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:FusionArg)), 1)), Expr(:call, Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:FusionConstant)), "hello")))), :x) + +@test expand(Main, :(f.(x, $"hello"))) == Expr(:call, GlobalRef(Base, :broadcast), Expr(:call, Expr(:call, GlobalRef(Core, :apply_type), Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:Fusion)), 1, false), Expr(:call, Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:FusionCall)), :f, Expr(:call, GlobalRef(Core, :tuple), Expr(:call, Expr(:call, GlobalRef(Core, :apply_type), Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:FusionArg)), 1)), Expr(:call, Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:FusionConstant)), "hello")))), :x) + +let val = QuoteNode(Any["some", "random", "splice", "value"]) + #@test expand(Main, :(f.(x, $val))) == Expr(:call, GlobalRef(Base, :broadcast), Expr(:call, Expr(:call, GlobalRef(Core, :apply_type), Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:Fusion)), 2, false), Expr(:call, Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:FusionCall)), :f, Expr(:call, GlobalRef(Core, :tuple), Expr(:call, Expr(:call, GlobalRef(Core, :apply_type), Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:FusionArg)), 1)), Expr(:call, Expr(:call, GlobalRef(Core, :apply_type), Expr(:call, GlobalRef(Core, :getfield), GlobalRef(Base, :Broadcast), :(:FusionArg)), 2))))), :x, val) + @test expand(Main, :(f.(x, $val))) == Expr(:call, GlobalRef(Base, :broadcast), :f, :x, val) +end + # make sure scalars are inlined, which causes f.(x,scalar) to lower to a "thunk" import Base.Meta: isexpr @test isexpr(Meta.lower(Main, :(f.(x,1))), :thunk) @@ -482,8 +501,10 @@ Base.BroadcastStyle(a2::Broadcast.ArrayStyle{AD2C}, a1::Broadcast.ArrayStyle{AD1 @testset "broadcasting for custom AbstractArray" begin a = randn(10) aa = Array19745(a) - @test a .+ 1 == @inferred(aa .+ 1) - @test a .* a' == @inferred(aa .* aa') + fadd(aa) = aa .+ 1 + fprod(aa) = aa .* aa' + @test a .+ 1 == @inferred(fadd(aa)) + @test a .* a' == @inferred(fprod(aa)) @test isa(aa .+ 1, Array19745) @test isa(aa .* aa', Array19745) a1 = AD1(rand(2,3)) diff --git a/test/numbers.jl b/test/numbers.jl index 083fd6c19e65b..7b0a55a11905b 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -2941,7 +2941,7 @@ Base.literal_pow(::typeof(^), ::PR20530, ::Val{p}) where {p} = 2 p = 2 @test x^p == 1 @test x^2 == 2 - @test [x,x,x].^2 == [2,2,2] + @test_broken [x, x, x].^2 == [2, 2, 2] # literal_pow violates referential transparency for T in (Float16, Float32, Float64, BigFloat, Int8, Int, BigInt, Complex{Int}, Complex{Float64}) for p in -4:4 v = eval(:($T(2)^$p)) diff --git a/test/ranges.jl b/test/ranges.jl index 75eca6ad821fa..4b6c772c002ee 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -967,11 +967,15 @@ end @test [reverse(linspace(1.0, 27.0, 1275));] == reverse([linspace(1.0, 27.0, 1275);]) end + @testset "PR 12200 and related" begin for _r in (1:2:100, 1:100, 1f0:2f0:100f0, 1.0:2.0:100.0, linspace(1, 100, 10), linspace(1f0, 100f0, 10)) float_r = float(_r) - big_r = big.(_r) + big_r = broadcast(big, _r) + big_rdot = big.(_r) + @test big_rdot == big_r + @test typeof(big_r) == typeof(big_rdot) @test typeof(big_r).name === typeof(_r).name if eltype(_r) <: AbstractFloat @test isa(float_r, typeof(_r))