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

RFC: EnumSet type #19470

Closed
wants to merge 7 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
151 changes: 149 additions & 2 deletions base/Enums.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
module Enums

import Core.Intrinsics.box
export Enum, @enum
export AbstractEnum, Enum, @enum, FlagEnum, @flagenum

abstract Enum
abstract AbstractEnum
abstract Enum <: AbstractEnum

Base.convert{T<:Integer}(::Type{T}, x::Enum) = convert(T, box(Int32, x))

Expand Down Expand Up @@ -140,4 +141,150 @@ macro enum(T,syms...)
return blk
end



abstract FlagEnum <: AbstractEnum

Base.convert{T<:Integer}(::Type{T}, x::FlagEnum) = convert(T, convert(Unsigned, x))
for op in (:|, :&, :xor)
@eval function Base.$op{T<:FlagEnum}(x::T,y::T)
reinterpret(T, ($op)(convert(Unsigned, x), convert(Unsigned, y)))
end
end
Base.in{T<:FlagEnum}(x::T, y::T) = x & y == x
Base.~{T<:FlagEnum}(x::T) = reinterpret(T, convert(Unsigned, typemax(T)) & ~convert(Unsigned, x))


"""
@flagenum EnumName[::U] enumvalue1[=x] enumvalue1[=y]

Create a `FlagEnum` type with name `EnumName` and base member values of `enumvalue1` and
`enumvalue1`, based on the unsigned integer type `U` (`UInt32` by default). If the values
`x` and `y` are provided, they must each have exactly a single bit on, and naturally, not
coincide. The `EnumName` type can be used just like other types, and enum member values as
regular values, such as

```jldoctest
julia> @flagenum FruitFlags apple=1<<0 orange=1<<1 kiwi=1<<2

julia> f(x::FruitFlags) = "I'm a FruitFlags with value: \$(Int(x))"
f (generic function with 1 method)

julia> f(apple|kiwi)
"I'm a FruitFlags with value: 5"
```
"""
macro flagenum(T,syms...)
if isempty(syms)
throw(ArgumentError("no arguments given for FlagEnum $T"))
end
if isa(T,Symbol)
typename = T
basetype = UInt32
elseif isa(T,Expr) && T.head == :(::) && length(T.args) == 2 && isa(T.args[1], Symbol)
typename = T.args[1]
basetype = eval(current_module(),T.args[2])
if !isa(basetype, DataType) || !(basetype <: Unsigned) || !isbits(basetype)
throw(ArgumentError("invalid base type for Enum $typename, $T=::$basetype; base type must be an unsigned integer bitstype"))
end
else
throw(ArgumentError("invalid type expression for FlagEnum $T"))
end
vals = Array{Tuple{Symbol,basetype}}(0)
mask = zero(basetype)
for s in syms
if isa(s,Symbol)
if mask & prevpow2(typemax(basetype)) != 0
throw(ArgumentError("overflow in value \"$s\" of FlagEnum $typename"))
end
i = max(prevpow2(mask) << 1, 1)
elseif isa(s,Expr) &&
(s.head == :(=) || s.head == :kw) &&
length(s.args) == 2 && isa(s.args[1],Symbol)
i = eval(current_module(),s.args[2]) # allow exprs, e.g. uint128"1"
if !isa(i, Integer)
throw(ArgumentError("invalid value for FlagEnum $typename, $s=$i; values must be integers"))
end
i = convert(basetype, i)
if count_ones(i) != 1
throw(ArgumentError("invalid value for FlagEnum $typename, $s=$i; value must have eactly 1 bit set"))
elseif mask & i != 0
throw(ArgumentError("invalid value for FlagEnum $typename, $s=$i; overlap with earlier value"))
end
s = s.args[1]
hasexpr = true
else
throw(ArgumentError(string("invalid argument for Enum ", typename, ": ", s)))
end
if !Base.isidentifier(s)
throw(ArgumentError("invalid name for Enum $typename; \"$s\" is not a valid identifier."))
end
push!(vals, (s,i))
mask |= i
end
values = basetype[i[2] for i in vals]
blk = quote
# enum definition
Base.@__doc__(bitstype $(sizeof(basetype) * 8) $(esc(typename)) <: FlagEnum)
$(esc(typename))() = reinterpret($(esc(typename)), zero($basetype))
function Base.convert(::Type{$(esc(typename))}, x::Integer)
if 0 <= x <= $mask &&
(xx = convert($basetype, x); xx & $mask == xx)
return reinterpret($(esc(typename)), xx)
else
throw(ArgumentError(string($"invalid value for Enum $(typename): ", x)))
end
end
Base.convert(::Type{$basetype}, x::$(esc(typename))) = reinterpret($basetype, x)
Base.convert(::Type{Unsigned}, x::$(esc(typename))) = reinterpret($basetype, x)
Base.typemin(x::Type{$(esc(typename))}) = $(esc(typename))(0)
Base.typemax(x::Type{$(esc(typename))}) = $(esc(typename))($mask)
let insts = ntuple(i->$(esc(typename))($values[i]), $(length(vals)))
Base.instances(::Type{$(esc(typename))}) = insts
end
function Base.print(io::IO, x::$(esc(typename)))
showcompact(io, typeof(x))
print(io, '(')
first = true
for (sym, i) in $vals
if i & $basetype(x) != 0
if first
first = false
else
print(io, '|')
end
print(io, sym)
end
end
print(io, ')')
end
function Base.show(io::IO, x::$(esc(typename)))
print(io, x)
if !get(io, :compact, false)
print(io, " = ")
show(io, $basetype(x))
end
end
function Base.show(io::IO, t::Type{$(esc(typename))})
Base.show_datatype(io, t)
end
function Base.show(io::IO, ::MIME"text/plain", t::Type{$(esc(typename))})
print(io, "FlagEnum ")
Base.show_datatype(io, t)
print(io, ":")
for (sym, i) in $vals
print(io, "\n", sym, " = ")
show(io, i)
end
end
end
for (sym,i) in vals
push!(blk.args, :(const $(esc(sym)) = $(esc(typename))($i)))
end
push!(blk.args, :nothing)
blk.head = :toplevel
return blk
end


end # module
3 changes: 3 additions & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export

# Types
AbstractChannel,
AbstractEnum,
AbstractMatrix,
AbstractSet,
AbstractUnitRange,
Expand Down Expand Up @@ -62,6 +63,7 @@ export
Enumerate,
Factorization,
FileMonitor,
FlagEnum,
FloatRange,
Future,
Hermitian,
Expand Down Expand Up @@ -1409,6 +1411,7 @@ export

@assert,
@enum,
@flagenum,
Copy link
Contributor

Choose a reason for hiding this comment

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

new exports need to be listed in a docs index to go into the new manual I believe

Copy link
Member

Choose a reason for hiding this comment

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

Yes, should be added after stdlib/base.md:114 where @enum's docstring is.

@label,
@goto,
@view,
Expand Down
87 changes: 45 additions & 42 deletions base/libgit2/consts.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,36 +27,38 @@ module Consts
const REF_LISTALL = REF_OID | REF_SYMBOLIC

# checkout
const CHECKOUT_NONE = Cuint(0)
const CHECKOUT_SAFE = Cuint(1 << 0)
const CHECKOUT_FORCE = Cuint(1 << 1)
const CHECKOUT_RECREATE_MISSING = Cuint(1 << 2)
const CHECKOUT_ALLOW_CONFLICTS = Cuint(1 << 4)
const CHECKOUT_REMOVE_UNTRACKED = Cuint(1 << 5)
const CHECKOUT_REMOVE_IGNORED = Cuint(1 << 6)
const CHECKOUT_UPDATE_ONLY = Cuint(1 << 7)
const CHECKOUT_DONT_UPDATE_INDEX = Cuint(1 << 8)
const CHECKOUT_NO_REFRESH = Cuint(1 << 9)
const CHECKOUT_SKIP_UNMERGED = Cuint(1 << 10)
const CHECKOUT_USE_OURS = Cuint(1 << 11)
const CHECKOUT_USE_THEIRS = Cuint(1 << 12)
const CHECKOUT_DISABLE_PATHSPEC_MATCH = Cuint(1 << 13)
const CHECKOUT_SKIP_LOCKED_DIRECTORIES = Cuint(1 << 18)
const CHECKOUT_DONT_OVERWRITE_IGNORED = Cuint(1 << 19)
const CHECKOUT_CONFLICT_STYLE_MERGE = Cuint(1 << 20)
const CHECKOUT_CONFLICT_STYLE_DIFF3 = Cuint(1 << 21)
const CHECKOUT_DONT_REMOVE_EXISTING = Cuint(1 << 22)
@flagenum(CHECKOUT,
CHECKOUT_SAFE = 1 << 0,
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it necessary to specify the values here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, as not all values are valid (e.g. 1 << 3).

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks, I didn't see that some where left out.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Some of the ones further down are unnecessary, but part of me thinks it might be good practice to leave them in for reference to the headers.

CHECKOUT_FORCE = 1 << 1,
CHECKOUT_RECREATE_MISSING = 1 << 2,
CHECKOUT_ALLOW_CONFLICTS = 1 << 4,
CHECKOUT_REMOVE_UNTRACKED = 1 << 5,
CHECKOUT_REMOVE_IGNORED = 1 << 6,
CHECKOUT_UPDATE_ONLY = 1 << 7,
CHECKOUT_DONT_UPDATE_INDEX = 1 << 8,
CHECKOUT_NO_REFRESH = 1 << 9,
CHECKOUT_SKIP_UNMERGED = 1 << 10,
CHECKOUT_USE_OURS = 1 << 11,
CHECKOUT_USE_THEIRS = 1 << 12,
CHECKOUT_DISABLE_PATHSPEC_MATCH = 1 << 13,
CHECKOUT_SKIP_LOCKED_DIRECTORIES = 1 << 18,
CHECKOUT_DONT_OVERWRITE_IGNORED = 1 << 19,
CHECKOUT_CONFLICT_STYLE_MERGE = 1 << 20,
CHECKOUT_CONFLICT_STYLE_DIFF3 = 1 << 21,
CHECKOUT_DONT_REMOVE_EXISTING = 1 << 22)
const CHECKOUT_NONE = CHECKOUT()

const CHECKOUT_UPDATE_SUBMODULES = Cuint(1 << 16)
const CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED = Cuint(1 << 17)

const CHECKOUT_NOTIFY_NONE = Cuint(0)
const CHECKOUT_NOTIFY_CONFLICT = Cuint(1 << 0)
const CHECKOUT_NOTIFY_DIRTY = Cuint(1 << 1)
const CHECKOUT_NOTIFY_UPDATED = Cuint(1 << 2)
const CHECKOUT_NOTIFY_UNTRACKED = Cuint(1 << 3)
const CHECKOUT_NOTIFY_IGNORED = Cuint(1 << 4)
const CHECKOUT_NOTIFY_ALL = 0x0FFFF
@flagenum(CHECKOUT_NOTIFY,
CHECKOUT_NOTIFY_CONFLICT = 1 << 0,
CHECKOUT_NOTIFY_DIRTY = 1 << 1,
CHECKOUT_NOTIFY_UPDATED = 1 << 2,
CHECKOUT_NOTIFY_UNTRACKED = 1 << 3,
CHECKOUT_NOTIFY_IGNORED = 1 << 4)
const CHECKOUT_NOTIFY_NONE = CHECKOUT_NOTIFY()
const CHECKOUT_NOTIFY_ALL = typemax(CHECKOUT_NOTIFY)

# diff
const DIFF_OPTIONS_VERSION = Cuint(1)
Expand Down Expand Up @@ -222,22 +224,23 @@ module Consts
const STATUS_SHOW_WORKDIR_ONLY = Cint(2)

# status options
const STATUS_OPT_INCLUDE_UNTRACKED = Cuint(1 << 0)
const STATUS_OPT_INCLUDE_IGNORED = Cuint(1 << 1)
const STATUS_OPT_INCLUDE_UNMODIFIED = Cuint(1 << 2)
const STATUS_OPT_EXCLUDE_SUBMODULES = Cuint(1 << 3)
const STATUS_OPT_RECURSE_UNTRACKED_DIRS = Cuint(1 << 4)
const STATUS_OPT_DISABLE_PATHSPEC_MATCH = Cuint(1 << 5)
const STATUS_OPT_RECURSE_IGNORED_DIRS = Cuint(1 << 6)
const STATUS_OPT_RENAMES_HEAD_TO_INDEX = Cuint(1 << 7)
const STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = Cuint(1 << 8)
const STATUS_OPT_SORT_CASE_SENSITIVELY = Cuint(1 << 9)
const STATUS_OPT_SORT_CASE_INSENSITIVELY = Cuint(1 << 10)
const STATUS_OPT_RENAMES_FROM_REWRITES = Cuint(1 << 11)
const STATUS_OPT_NO_REFRESH = Cuint(1 << 12)
const STATUS_OPT_UPDATE_INDEX = Cuint(1 << 13)
const STATUS_OPT_INCLUDE_UNREADABLE = Cuint(1 << 14)
const STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED = Cuint(1 << 15)
@flagenum(STATUS_OPT,
STATUS_OPT_INCLUDE_UNTRACKED = 1 << 0,
STATUS_OPT_INCLUDE_IGNORED = 1 << 1,
STATUS_OPT_INCLUDE_UNMODIFIED = 1 << 2,
STATUS_OPT_EXCLUDE_SUBMODULES = 1 << 3,
STATUS_OPT_RECURSE_UNTRACKED_DIRS = 1 << 4,
STATUS_OPT_DISABLE_PATHSPEC_MATCH = 1 << 5,
STATUS_OPT_RECURSE_IGNORED_DIRS = 1 << 6,
STATUS_OPT_RENAMES_HEAD_TO_INDEX = 1 << 7,
STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = 1 << 8,
STATUS_OPT_SORT_CASE_SENSITIVELY = 1 << 9,
STATUS_OPT_SORT_CASE_INSENSITIVELY = 1 << 10,
STATUS_OPT_RENAMES_FROM_REWRITES = 1 << 11,
STATUS_OPT_NO_REFRESH = 1 << 12,
STATUS_OPT_UPDATE_INDEX = 1 << 13,
STATUS_OPT_INCLUDE_UNREADABLE = 1 << 14,
STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED = 1 << 15)

@enum(GIT_SUBMODULE_IGNORE, SUBMODULE_IGNORE_UNSPECIFIED = -1, # use the submodule's configuration
SUBMODULE_IGNORE_NONE = 1, # any change or untracked == dirty
Expand Down
18 changes: 9 additions & 9 deletions base/libgit2/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,14 @@ reset!(p::AbstractCredentials, cnt::Int=3) = nothing
immutable CheckoutOptions
version::Cuint

checkout_strategy::Cuint
checkout_strategy::Consts.CHECKOUT

disable_filters::Cint
dir_mode::Cuint
file_mode::Cuint
file_open_flags::Cint

notify_flags::Cuint
notify_flags::Consts.CHECKOUT_NOTIFY
notify_cb::Ptr{Void}
notify_payload::Ptr{Void}

Expand All @@ -89,12 +89,12 @@ immutable CheckoutOptions
perfdata_cb::Ptr{Void}
perfdata_payload::Ptr{Void}
end
CheckoutOptions(; checkout_strategy::Cuint = Consts.CHECKOUT_SAFE,
CheckoutOptions(; checkout_strategy::Consts.CHECKOUT = Consts.CHECKOUT_SAFE,
disable_filters::Cint = zero(Cint),
dir_mode::Cuint = Cuint(0), # Cuint(0o755),
file_mode::Cuint = Cuint(0), #Cuint(0o644),
file_open_flags::Cint = zero(Cint),
notify_flags::Cuint = Consts.CHECKOUT_NOTIFY_NONE,
notify_flags::Consts.CHECKOUT_NOTIFY = Consts.CHECKOUT_NOTIFY_NONE,
notify_cb::Ptr{Void} = Ptr{Void}(0),
notify_payload::Ptr{Void} = Ptr{Void}(0),
progress_cb::Ptr{Void} = Ptr{Void}(0),
Expand Down Expand Up @@ -541,14 +541,14 @@ Base.show(io::IO, rbo::RebaseOperation) = print(io, "RebaseOperation($(string(rb
immutable StatusOptions
version::Cuint
show::Cint
flags::Cuint
flags::Consts.STATUS_OPT
pathspec::StrArrayStruct
end
StatusOptions(; show::Cint = Consts.STATUS_SHOW_INDEX_AND_WORKDIR,
flags::Cuint = Consts.STATUS_OPT_INCLUDE_UNTRACKED |
Consts.STATUS_OPT_RECURSE_UNTRACKED_DIRS |
Consts.STATUS_OPT_RENAMES_HEAD_TO_INDEX |
Consts.STATUS_OPT_SORT_CASE_SENSITIVELY,
flags::Consts.STATUS_OPT = Consts.STATUS_OPT_INCLUDE_UNTRACKED |
Copy link
Contributor

Choose a reason for hiding this comment

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

Indent on the following lines.

Consts.STATUS_OPT_RECURSE_UNTRACKED_DIRS |
Consts.STATUS_OPT_RENAMES_HEAD_TO_INDEX |
Consts.STATUS_OPT_SORT_CASE_SENSITIVELY,
pathspec::StrArrayStruct = StrArrayStruct()) =
StatusOptions(one(Cuint),
show,
Expand Down
2 changes: 1 addition & 1 deletion base/set.jl
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ function issubset(l, r)
end
const ⊆ = issubset
⊊(l::Set, r::Set) = <(l, r)
⊈(l::Set, r::Set) = !⊆(l, r)
⊈(l, r) = !⊆(l, r)
Copy link
Member

Choose a reason for hiding this comment

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

Does the new type support issubset?


"""
unique(itr)
Expand Down
1 change: 1 addition & 0 deletions doc/src/stdlib/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ Base.typejoin
Base.typeintersect
Base.Val
Base.Enums.@enum
Base.Enums.@flagenum
Copy link
Contributor

Choose a reason for hiding this comment

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

not this PR's fault, but this seems like it's in kind of an odd section

Base.instances
```

Expand Down
Loading