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

implement getfield overloading #24960

Merged
merged 3 commits into from
Dec 18, 2017
Merged
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
15 changes: 9 additions & 6 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ New language features
iterated using `pairs(kw)`. `kw` can no longer contain multiple entries for the same
argument name ([#4916]).

* Custom infix operators can now be defined by appending Unicode
combining marks, primes, and sub/superscripts to other operators.
For example, `+̂ₐ″` is parsed as an infix operator with the same
precedence as `+` ([#22089]).
* Custom infix operators can now be defined by appending Unicode
combining marks, primes, and sub/superscripts to other operators.
For example, `+̂ₐ″` is parsed as an infix operator with the same
precedence as `+` ([#22089]).

* The macro call syntax `@macroname[args]` is now available and is parsed
as `@macroname([args])` ([#23519]).
* The macro call syntax `@macroname[args]` is now available and is parsed
as `@macroname([args])` ([#23519]).

* The construct `if @generated ...; else ...; end` can be used to provide both
`@generated` and normal implementations of part of a function. Surrounding code
Expand All @@ -35,6 +35,9 @@ New language features
missing values ([#24653]). It propagates through standard operators and mathematical functions,
and implements three-valued logic, similar to SQLs `NULL` and R's `NA`.

* Field access via dot-syntax can now be overloaded by adding methods to
`Base.getproperty` and `Base.setproperty!` ([#1974]).

Language changes
----------------

Expand Down
6 changes: 6 additions & 0 deletions base/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ import Core: arraysize, arrayset, arrayref
vect() = Vector{Any}()
vect(X::T...) where {T} = T[ X[i] for i = 1:length(X) ]

"""
vect(X...)

Create a Vector with element type computed from the promote_typeof of the argument,
containing the argument list.
"""
function vect(X...)
T = promote_typeof(X...)
#T[ X[i] for i=1:length(X) ]
Expand Down
3 changes: 3 additions & 0 deletions base/boot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@ export
# constants
nothing, Main

const getproperty = getfield
const setproperty! = setfield!

abstract type Number end
abstract type Real <: Number end
abstract type AbstractFloat <: Real end
Expand Down
5 changes: 4 additions & 1 deletion base/coreimg.jl
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license

Main.Core.eval(Main.Core, :(baremodule Inference
getfield(getfield(Main, :Core), :eval)(getfield(Main, :Core), :(baremodule Inference
using Core.Intrinsics
import Core: print, println, show, write, unsafe_write, STDOUT, STDERR

const getproperty = getfield
const setproperty! = setfield!

ccall(:jl_set_istopmod, Void, (Any, Bool), Inference, false)

eval(x) = Core.eval(Inference, x)
Expand Down
31 changes: 27 additions & 4 deletions base/docs/basedocs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1236,8 +1236,8 @@ tuple
"""
getfield(value, name::Symbol)

Extract a named field from a `value` of composite type. The syntax `a.b` calls
`getfield(a, :b)`.
Extract a named field from a `value` of composite type.
See also [`getproperty`](@ref Base.getproperty).

# Examples
```jldoctest
Expand All @@ -1256,8 +1256,9 @@ getfield
"""
setfield!(value, name::Symbol, x)

Assign `x` to a named field in `value` of composite type. The syntax `a.b = c` calls
`setfield!(a, :b, c)`. `value` must be mutable.
Assign `x` to a named field in `value` of composite type.
The `value` must be mutable and `x` must be a subtype of `fieldtype(typeof(value), name)`.
See also [`setproperty!`](@ref Base.setproperty!).

# Examples
```jldoctest
Expand Down Expand Up @@ -1768,4 +1769,26 @@ The base library of Julia.
"""
kw"Base"

"""
typeassert(x, type)

Throw a TypeError unless `x isa type`.
The syntax `x::type` calls this function.
"""
typeassert

"""
getproperty(value, name::Symbol)

The syntax `a.b` calls `getproperty(a, :b)`.
"""
Base.getproperty

"""
setproperty!(value, name::Symbol, x)

The syntax `a.b = c` calls `setproperty!(a, :b, c)`.
"""
Base.setproperty!

end
2 changes: 2 additions & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -898,6 +898,8 @@ export

# types
convert,
# getproperty,
# setproperty!,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

export?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keno asked me not to do so initially.

fieldoffset,
fieldname,
fieldnames,
Expand Down
17 changes: 10 additions & 7 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1961,7 +1961,7 @@ function abstract_call_gf_by_type(@nospecialize(f), argtypes::Vector{Any}, @nosp
# if there's a possibility we could constant-propagate a better result
# (hopefully without doing too much work), try to do that now
# TODO: it feels like this could be better integrated into abstract_call_method / typeinf_edge
const_rettype = abstract_call_method_with_const_args(argtypes, applicable[1]::SimpleVector, sv)
const_rettype = abstract_call_method_with_const_args(f, argtypes, applicable[1]::SimpleVector, sv)
if const_rettype ⊑ rettype
# use the better result, if it's a refinement of rettype
rettype = const_rettype
Expand Down Expand Up @@ -2020,7 +2020,7 @@ function cache_lookup(code::MethodInstance, argtypes::Vector{Any}, cache::Vector
return nothing
end

function abstract_call_method_with_const_args(argtypes::Vector{Any}, match::SimpleVector, sv::InferenceState)
function abstract_call_method_with_const_args(@nospecialize(f), argtypes::Vector{Any}, match::SimpleVector, sv::InferenceState)
method = match[3]::Method
nargs::Int = method.nargs
method.isva && (nargs -= 1)
Expand Down Expand Up @@ -2053,11 +2053,14 @@ function abstract_call_method_with_const_args(argtypes::Vector{Any}, match::Simp
end
end
if !cache_inlineable && !sv.params.aggressive_constant_propagation
# in this case, see if all of the arguments are constants
for i in 1:nargs
a = argtypes[i]
if !isa(a, Const) && !isconstType(a)
return Any
tm = _topmod(sv)
if !istopfunction(tm, f, :getproperty) && !istopfunction(tm, f, :setproperty!)
# in this case, see if all of the arguments are constants
for i in 1:nargs
a = argtypes[i]
if !isa(a, Const) && !isconstType(a)
return Any
end
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion base/libgit2/gitcredential.jl
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ function Base.read!(io::IO, cred::GitCredential)
# https://git-scm.com/docs/git-credential#git-credential-codeurlcode
copy!(cred, parse(GitCredential, value))
else
setfield!(cred, Symbol(key), String(value))
Base.setproperty!(cred, Symbol(key), String(value))
end
end

Expand Down
2 changes: 1 addition & 1 deletion base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ function show_default(io::IO, @nospecialize(x))
if !isdefined(x, f)
print(io, undef_ref_str)
else
show(recur_io, getfield(x, f))
show(recur_io, getfield(x, i))
end
if i < nf
print(io, ", ")
Expand Down
11 changes: 11 additions & 0 deletions base/sysimg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ baremodule Base
using Core.Intrinsics
ccall(:jl_set_istopmod, Void, (Any, Bool), Base, true)

getproperty(x, f::Symbol) = getfield(x, f)
setproperty!(x, f::Symbol, v) = setfield!(x, f, convert(fieldtype(typeof(x), f), v))

# Try to help prevent users from shooting them-selves in the foot
# with ambiguities by defining a few common and critical operations
# (and these don't need the extra convert code)
getproperty(x::Module, f::Symbol) = getfield(x, f)
setproperty!(x::Module, f::Symbol, v) = setfield!(x, f, v)
getproperty(x::Type, f::Symbol) = getfield(x, f)
setproperty!(x::Type, f::Symbol, v) = setfield!(x, f, v)

function include(mod::Module, path::AbstractString)
local result
if INCLUDE_STATE === 1
Expand Down
22 changes: 12 additions & 10 deletions doc/src/manual/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,16 +158,18 @@ Under the name `f`, the function does not support infix notation, however.

A few special expressions correspond to calls to functions with non-obvious names. These are:

| Expression | Calls |
|:----------------- |:---------------------- |
| `[A B C ...]` | [`hcat`](@ref) |
| `[A; B; C; ...]` | [`vcat`](@ref) |
| `[A B; C D; ...]` | [`hvcat`](@ref) |
| `A'` | [`adjoint`](@ref) |
| `A.'` | [`transpose`](@ref) |
| `1:n` | [`colon`](@ref) |
| `A[i]` | [`getindex`](@ref) |
| `A[i]=x` | [`setindex!`](@ref) |
| Expression | Calls |
|:----------------- |:----------------------- |
| `[A B C ...]` | [`hcat`](@ref) |
| `[A; B; C; ...]` | [`vcat`](@ref) |
| `[A B; C D; ...]` | [`hvcat`](@ref) |
| `A'` | [`adjoint`](@ref) |
| `A.'` | [`transpose`](@ref) |
| `1:n` | [`colon`](@ref) |
| `A[i]` | [`getindex`](@ref) |
| `A[i] = x` | [`setindex!`](@ref) |
| `A.n` | [`getproperty`](@ref Base.getproperty) |
| `A.n = x` | [`setproperty!`](@ref Base.setproperty!) |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We really need more documentation on how to use this, explaining the arguments to getproperty, how to fall back to getfield, implications for performance and type stability, best practices....

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, there are zero tests, so some tests would be good...

Copy link
Member

@ihnorton ihnorton Dec 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stevengj FWIW I played with this a bit last night for PyCall, and just put up what I have so far in case it helps (won't be able to look again until tonight or tomorrow). The getfield fallback is clearly suboptimal -- we should probably change all of the PyObject.o calls, while I only did a few -- and no setproperty! support, but the following works:

julia> np = pyimport("numpy")
PyObject <module 'numpy' from '/Users/inorton/.julia/v0.7/Conda/deps/usr/lib/python2.7/site-packages/numpy/__init__.pyc'>

julia> np.sin(1:100);

https://github.com/ihnorton/PyCall.jl/tree/getfield_parasido

Copy link
Member

@stevengj stevengj Dec 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ihnorton, thanks for looking into this! Yes, my plan is to release a PyCall 2.0 for Julia 0.7, and switch over completely to getproperty (finally dropping/deprecating @pyimport and pywrap). But I'm not planning to work on it until 0.7 is close to release.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, there are zero tests, so some tests would be good...

I wrote them. Apparently I forgot to forgot to push them.


## [Anonymous Functions](@id man-anonymous-functions)

Expand Down
1 change: 1 addition & 0 deletions doc/src/stdlib/arrays.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ Base.cat
Base.vcat
Base.hcat
Base.hvcat
Base.vect
Base.flipdim
Base.circshift
Base.circshift!
Expand Down
9 changes: 6 additions & 3 deletions doc/src/stdlib/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ Base.isless
Base.ifelse
Base.lexcmp
Base.lexless
Core.typeassert
Core.typeof
Core.tuple
Base.ntuple
Expand All @@ -124,6 +125,10 @@ Base.finalizer
Base.finalize
Base.copy
Base.deepcopy
Base.getproperty
Base.setproperty!
Core.getfield
Core.setfield!
Core.isdefined
Base.@isdefined
Base.convert
Expand All @@ -133,7 +138,7 @@ Base.widen
Base.identity
```

## Dealing with Types
## Properties of Types

```@docs
Base.supertype
Expand All @@ -150,8 +155,6 @@ Base.eps(::Type{<:AbstractFloat})
Base.eps(::AbstractFloat)
Base.promote_type
Base.promote_rule
Core.getfield
Core.setfield!
Base.fieldoffset
Core.fieldtype
Base.isimmutable
Expand Down
30 changes: 15 additions & 15 deletions doc/src/stdlib/punctuation.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

Extended documentation for mathematical symbols & functions is [here](@ref math-ops).

| symbol | meaning |
|:----------- |:------------------------------------------------------------------------------------------- |
| symbol | meaning |
|:----------- |:----------------------------------------------------------------------------------------------------------------------------------------------- |
| `@m` | invoke macro `m`; followed by space-separated expressions |
| `!` | prefix "not" operator |
| `a!( )` | at the end of a function name, `!` indicates that a function modifies its argument(s) |
| `!` | prefix "not" (logical negation) operator |
| `a!( )` | at the end of a function name, `!` is used as a convention to indicate that a function modifies its argument(s) |
| `#` | begin single line comment |
| `#=` | begin multi-line comment (these are nestable) |
| `=#` | end multi-line comment |
Expand All @@ -23,23 +23,23 @@ Extended documentation for mathematical symbols & functions is [here](@ref math-
| `~` | bitwise not operator |
| `\` | backslash operator |
| `'` | complex transpose operator Aᴴ |
| `a[]` | array indexing |
| `[,]` | vertical concatenation |
| `[;]` | also vertical concatenation |
| `[   ]` | with space-separated expressions, horizontal concatenation |
| `a[]` | array indexing (calling [`getindex`](@ref) or [`setindex!`](@ref)) |
| `[,]` | vector literal constructor (calling [`vect`](@ref Base.vect)) |
| `[;]` | vertical concatenation (calling [`vcat`](@ref) or [`hvcat`](@ref)) |
| `[   ]` | with space-separated expressions, horizontal concatenation (calling [`hcat`](@ref) or [`hvcat`](@ref)) |
| `T{ }` | parametric type instantiation |
| `;` | statement separator |
| `,` | separate function arguments or tuple components |
| `?` | 3-argument conditional operator (conditional ? if_true : if_false) |
| `?` | 3-argument conditional operator (used like: `conditional ? if_true : if_false`) |
| `""` | delimit string literals |
| `''` | delimit character literals |
| ``` ` ` ``` | delimit external process (command) specifications |
| `...` | splice arguments into a function call or declare a varargs function or type |
| `.` | access named fields in objects/modules, also prefixes elementwise operator/function calls |
| `a:b` | range a, a+1, a+2, ..., b |
| `a:s:b` | range a, a+s, a+2s, ..., b |
| `:` | index an entire dimension (1:end) |
| `::` | type annotation, depending on context |
| `...` | splice arguments into a function call or declare a varargs function |
| `.` | access named fields in objects/modules (calling [`getproperty`](@ref Base.getproperty) or [`setproperty!`](@ref Base.setproperty!)), also prefixes elementwise function calls (calling [`broadcast`](@ref)) |
| `a:b` | range a, a+1, a+2, ..., b (calling [`colon`](@ref)) |
| `a:s:b` | range a, a+s, a+2s, ..., b (also calling [`colon`](@ref)) |
| `:` | index an entire dimension (1:endof), see [`Colon`](@ref)) |
| `::` | type annotation or [`typeassert`](@ref), depending on context |
| `:( )` | quoted expression |
| `:a` | symbol a |
| `<:` | [`subtype operator`](@ref <:) |
Expand Down
12 changes: 7 additions & 5 deletions src/julia-syntax.scm
Original file line number Diff line number Diff line change
Expand Up @@ -1752,7 +1752,7 @@
(if (and (pair? e) (eq? (car 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))
`(call (top getproperty) ,f ,x))
((eq? (car x) 'tuple)
(make-fuse f (cdr x)))
(else
Expand Down Expand Up @@ -2039,10 +2039,12 @@
,.(if (eq? aa a) '() `((= ,aa ,(expand-forms a))))
,.(if (eq? bb b) '() `((= ,bb ,(expand-forms b))))
,.(if (eq? rr rhs) '() `((= ,rr ,(expand-forms rhs))))
(call (core setfield!) ,aa ,bb
(call (top convert)
(call (core fieldtype) (call (core typeof) ,aa) ,bb)
,rr))
(call (top setproperty!) ,aa ,bb
(if (call (top ===) (top setproperty!) (core setfield!))
(call (top convert)
(call (core fieldtype) (call (core typeof) ,aa) ,bb)
,rr)
,rr))
(unnecessary ,rr)))))
((tuple)
;; multiple assignment
Expand Down
Loading