Skip to content

Commit

Permalink
Remove no-op specializations of only [NFC] (JuliaLang#52328)
Browse files Browse the repository at this point in the history
An easily merged subset of @matthias314's JuliaLang#52296, separated from that PR
at @mkitti's suggestion.

I kept the low-but-nonzero value specializations for Tuple and Named
tuple which offer better error messages. They may be removed with a
future PR that improves the generic error message.

To verify this is indeed a no-op

```
@code_llvm only(Ref(4))
@code_llvm only(Ref(nothing))
@code_llvm only('z')
@code_llvm only((4,))
@code_llvm only((nothing,))
only((4,2))
@code_llvm only(Array{Int, 0}(undef))
@code_llvm only((;x=3))
only((;))
only((;x=2, y=4))
```
Before
```
julia> @code_llvm only(Ref(4))
; Function Signature: only(Base.RefValue{Int64})
;  @ iterators.jl:1563 within `only`
define i64 @julia_only_2451({}* noundef nonnull align 8 dereferenceable(8) %"x::RefValue") #0 {
top:
; ┌ @ refvalue.jl:59 within `getindex`
; │┌ @ Base.jl:49 within `getproperty`
    %0 = bitcast {}* %"x::RefValue" to i64*
    %.x = load i64, i64* %0, align 8
; └└
  ret i64 %.x
}

julia> @code_llvm only(Ref(nothing))
; Function Signature: only(Base.RefValue{Nothing})
;  @ iterators.jl:1563 within `only`
define void @julia_only_2519({}* noundef nonnull %"x::RefValue") #0 {
top:
  ret void
}

julia> @code_llvm only('z')
; Function Signature: only(Char)
;  @ iterators.jl:1565 within `only`
define i32 @julia_only_2527(i32 zeroext %"x::Char") #0 {
top:
  ret i32 %"x::Char"
}

julia> @code_llvm only((4,))
; Function Signature: only(Tuple{Int64})
;  @ iterators.jl:1566 within `only`
define i64 @julia_only_2529([1 x i64]* nocapture noundef nonnull readonly align 8 dereferenceable(8) %"x::Tuple") #0 {
top:
; ┌ @ tuple.jl:31 within `getindex`
   %"x::Tuple[1]_ptr" = getelementptr inbounds [1 x i64], [1 x i64]* %"x::Tuple", i64 0, i64 0
; └
  %"x::Tuple[1]_ptr.unbox" = load i64, i64* %"x::Tuple[1]_ptr", align 8
  ret i64 %"x::Tuple[1]_ptr.unbox"
}

julia> @code_llvm only((nothing,))
; Function Signature: only(Tuple{Nothing})
;  @ iterators.jl:1566 within `only`
define void @julia_only_2532() #0 {
top:
  ret void
}

julia> only((4,2))
ERROR: ArgumentError: Tuple contains 2 elements, must contain exactly 1 element
Stacktrace:
 [1] only(x::Tuple{Int64, Int64})
   @ Base.Iterators ./iterators.jl:1567
 [2] top-level scope
   @ REPL[6]:1

julia> @code_llvm only(Array{Int, 0}(undef))
; Function Signature: only(Array{Int64, 0})
;  @ iterators.jl:1570 within `only`
define i64 @julia_only_2779({}* noundef nonnull align 8 dereferenceable(16) %"a::Array") #0 {
top:
; ┌ @ abstractarray.jl:1314 within `getindex`
; │┌ @ abstractarray.jl:1343 within `_getindex`
; ││┌ @ essentials.jl:817 within `getindex`
     %0 = bitcast {}* %"a::Array" to i64**
     %1 = load i64*, i64** %0, align 8
     %2 = load i64, i64* %1, align 8
; └└└
  ret i64 %2
}

julia> @code_llvm only((;x=3))
; Function Signature: only(NamedTuple{(:x,), Tuple{Int64}})
;  @ iterators.jl:1571 within `only`
define i64 @julia_only_2794([1 x i64]* nocapture noundef nonnull readonly align 8 dereferenceable(8) %"x::NamedTuple") #0 {
top:
; ┌ @ abstractarray.jl:469 within `first`
; │┌ @ namedtuple.jl:165 within `iterate` @ namedtuple.jl:165
    %"x::NamedTuple.x_ptr" = getelementptr inbounds [1 x i64], [1 x i64]* %"x::NamedTuple", i64 0, i64 0
; └└
  %"x::NamedTuple.x_ptr.unbox" = load i64, i64* %"x::NamedTuple.x_ptr", align 8
  ret i64 %"x::NamedTuple.x_ptr.unbox"
}

julia> only((;))
ERROR: ArgumentError: NamedTuple contains 0 elements, must contain exactly 1 element
Stacktrace:
 [1] only(x::@NamedTuple{})
   @ Base.Iterators ./iterators.jl:1572
 [2] top-level scope
   @ REPL[9]:1

julia> only((;x=2, y=4))
ERROR: ArgumentError: NamedTuple contains 2 elements, must contain exactly 1 element
Stacktrace:
 [1] only(x::@NamedTuple{x::Int64, y::Int64})
   @ Base.Iterators ./iterators.jl:1572
 [2] top-level scope
   @ REPL[10]:1
```

After
```
julia> @code_llvm only(Ref(4))
; Function Signature: only(Base.RefValue{Int64})
;  @ iterators.jl:1550 within `only`
define i64 @julia_only_6821({}* noundef nonnull align 8 dereferenceable(8) %"x::RefValue") #0 {
top:
;  @ iterators.jl:1551 within `only`
; ┌ @ refpointer.jl:103 within `iterate`
; │┌ @ refvalue.jl:59 within `getindex`
; ││┌ @ Base.jl:49 within `getproperty`
     %0 = bitcast {}* %"x::RefValue" to i64*
     %.x = load i64, i64* %0, align 8
; └└└
;  @ iterators.jl:1559 within `only`
  ret i64 %.x
}

julia> @code_llvm only(Ref(nothing))
; Function Signature: only(Base.RefValue{Nothing})
;  @ iterators.jl:1550 within `only`
define void @julia_only_6824({}* noundef nonnull %"x::RefValue") #0 {
top:
;  @ iterators.jl:1559 within `only`
  ret void
}

julia> @code_llvm only('z')
; Function Signature: only(Char)
;  @ iterators.jl:1550 within `only`
define i32 @julia_only_6826(i32 zeroext %"x::Char") #0 {
top:
;  @ iterators.jl:1559 within `only`
  ret i32 %"x::Char"
}

julia> @code_llvm only((4,))
; Function Signature: only(Tuple{Int64})
;  @ /Users/x/.julia/dev/julia/base/iterators.jl:1566 within `only`
define i64 @julia_only_6833([1 x i64]* nocapture noundef nonnull readonly align 8 dereferenceable(8) %"x::Tuple") #0 {
top:
; ┌ @ tuple.jl:31 within `getindex`
   %"x::Tuple[1]_ptr" = getelementptr inbounds [1 x i64], [1 x i64]* %"x::Tuple", i64 0, i64 0
; └
  %"x::Tuple[1]_ptr.unbox" = load i64, i64* %"x::Tuple[1]_ptr", align 8
  ret i64 %"x::Tuple[1]_ptr.unbox"
}

julia> @code_llvm only((nothing,))
; Function Signature: only(Tuple{Nothing})
;  @ /Users/x/.julia/dev/julia/base/iterators.jl:1566 within `only`
define void @julia_only_6836() #0 {
top:
  ret void
}

julia> only((4,2))
ERROR: ArgumentError: Tuple contains 2 elements, must contain exactly 1 element
Stacktrace:
 [1] only(x::Tuple{Int64, Int64})
   @ Base.Iterators ~/.julia/dev/julia/base/iterators.jl:1564
 [2] top-level scope
   @ REPL[31]:1

julia> @code_llvm only(Array{Int, 0}(undef))
; Function Signature: only(Array{Int64, 0})
;  @ iterators.jl:1550 within `only`
define i64 @julia_only_6848({}* noundef nonnull align 8 dereferenceable(16) %"x::Array") #0 {
L60:
;  @ iterators.jl:1551 within `only`
; ┌ @ array.jl:884 within `iterate` @ array.jl:884
; │┌ @ essentials.jl:817 within `getindex`
    %0 = bitcast {}* %"x::Array" to i64**
    %1 = load i64*, i64** %0, align 8
    %2 = load i64, i64* %1, align 8
; └└
;  @ iterators.jl:1559 within `only`
  ret i64 %2
}

julia> @code_llvm only((;x=3))
; Function Signature: only(NamedTuple{(:x,), Tuple{Int64}})
;  @ /Users/x/.julia/dev/julia/base/iterators.jl:1571 within `only`
define i64 @julia_only_6871([1 x i64]* nocapture noundef nonnull readonly align 8 dereferenceable(8) %"x::NamedTuple") #0 {
top:
; ┌ @ abstractarray.jl:469 within `first`
; │┌ @ namedtuple.jl:165 within `iterate` @ namedtuple.jl:165
    %"x::NamedTuple.x_ptr" = getelementptr inbounds [1 x i64], [1 x i64]* %"x::NamedTuple", i64 0, i64 0
; └└
  %"x::NamedTuple.x_ptr.unbox" = load i64, i64* %"x::NamedTuple.x_ptr", align 8
  ret i64 %"x::NamedTuple.x_ptr.unbox"
}

julia> only((;))
ERROR: ArgumentError: NamedTuple contains 0 elements, must contain exactly 1 element
Stacktrace:
 [1] only(x::@NamedTuple{})
   @ Base.Iterators ~/.julia/dev/julia/base/iterators.jl:1568
 [2] top-level scope
   @ REPL[34]:1

julia> only((;x=2, y=4))
ERROR: ArgumentError: NamedTuple contains 2 elements, must contain exactly 1 element
Stacktrace:
 [1] only(x::@NamedTuple{x::Int64, y::Int64})
   @ Base.Iterators ~/.julia/dev/julia/base/iterators.jl:1568
 [2] top-level scope
   @ REPL[35]:1
```
Diff
```diff
< # Before
---
> # After
4,5c4,5
< ;  @ iterators.jl:1563 within `only`
< define i64 @julia_only_2451({}* noundef nonnull align 8 dereferenceable(8) %"x::RefValue") #0 {
---
> ;  @ iterators.jl:1550 within `only`
> define i64 @julia_only_6821({}* noundef nonnull align 8 dereferenceable(8) %"x::RefValue") #0 {
7,11c7,14
< ; ┌ @ refvalue.jl:59 within `getindex`
< ; │┌ @ Base.jl:49 within `getproperty`
<     %0 = bitcast {}* %"x::RefValue" to i64*
<     %.x = load i64, i64* %0, align 8
< ; └└
---
> ;  @ iterators.jl:1551 within `only`
> ; ┌ @ refpointer.jl:103 within `iterate`
> ; │┌ @ refvalue.jl:59 within `getindex`
> ; ││┌ @ Base.jl:49 within `getproperty`
>      %0 = bitcast {}* %"x::RefValue" to i64*
>      %.x = load i64, i64* %0, align 8
> ; └└└
> ;  @ iterators.jl:1559 within `only`
17,18c20,21
< ;  @ iterators.jl:1563 within `only`
< define void @julia_only_2519({}* noundef nonnull %"x::RefValue") #0 {
---
> ;  @ iterators.jl:1550 within `only`
> define void @julia_only_6824({}* noundef nonnull %"x::RefValue") #0 {
19a23
> ;  @ iterators.jl:1559 within `only`
25,26c29,30
< ;  @ iterators.jl:1565 within `only`
< define i32 @julia_only_2527(i32 zeroext %"x::Char") #0 {
---
> ;  @ iterators.jl:1550 within `only`
> define i32 @julia_only_6826(i32 zeroext %"x::Char") #0 {
27a32
> ;  @ iterators.jl:1559 within `only`
33,34c38,39
< ;  @ iterators.jl:1566 within `only`
< define i64 @julia_only_2529([1 x i64]* nocapture noundef nonnull readonly align 8 dereferenceable(8) %"x::Tuple") #0 {
---
> ;  @ /Users/x/.julia/dev/julia/base/iterators.jl:1566 within `only`
> define i64 @julia_only_6833([1 x i64]* nocapture noundef nonnull readonly align 8 dereferenceable(8) %"x::Tuple") #0 {
45,46c50,51
< ;  @ iterators.jl:1566 within `only`
< define void @julia_only_2532() #0 {
---
> ;  @ /Users/x/.julia/dev/julia/base/iterators.jl:1566 within `only`
> define void @julia_only_6836() #0 {
55c60
<    @ Base.Iterators ./iterators.jl:1567
---
>    @ Base.Iterators ~/.julia/dev/julia/base/iterators.jl:1564
57c62
<    @ REPL[6]:1
---
>    @ REPL[31]:1
61,70c66,76
< ;  @ iterators.jl:1570 within `only`
< define i64 @julia_only_2779({}* noundef nonnull align 8 dereferenceable(16) %"a::Array") #0 {
< top:
< ; ┌ @ abstractarray.jl:1314 within `getindex`
< ; │┌ @ abstractarray.jl:1343 within `_getindex`
< ; ││┌ @ essentials.jl:817 within `getindex`
<      %0 = bitcast {}* %"a::Array" to i64**
<      %1 = load i64*, i64** %0, align 8
<      %2 = load i64, i64* %1, align 8
< ; └└└
---
> ;  @ iterators.jl:1550 within `only`
> define i64 @julia_only_6848({}* noundef nonnull align 8 dereferenceable(16) %"x::Array") #0 {
> L60:
> ;  @ iterators.jl:1551 within `only`
> ; ┌ @ array.jl:884 within `iterate` @ array.jl:884
> ; │┌ @ essentials.jl:817 within `getindex`
>     %0 = bitcast {}* %"x::Array" to i64**
>     %1 = load i64*, i64** %0, align 8
>     %2 = load i64, i64* %1, align 8
> ; └└
> ;  @ iterators.jl:1559 within `only`
76,77c82,83
< ;  @ iterators.jl:1571 within `only`
< define i64 @julia_only_2794([1 x i64]* nocapture noundef nonnull readonly align 8 dereferenceable(8) %"x::NamedTuple") #0 {
---
> ;  @ /Users/x/.julia/dev/julia/base/iterators.jl:1571 within `only`
> define i64 @julia_only_6871([1 x i64]* nocapture noundef nonnull readonly align 8 dereferenceable(8) %"x::NamedTuple") #0 {
91c97
<    @ Base.Iterators ./iterators.jl:1572
---
>    @ Base.Iterators ~/.julia/dev/julia/base/iterators.jl:1568
93c99
<    @ REPL[9]:1
---
>    @ REPL[34]:1
99c105
<    @ Base.Iterators ./iterators.jl:1572
---
>    @ Base.Iterators ~/.julia/dev/julia/base/iterators.jl:1568
101c107
<    @ REPL[10]:1
---
>    @ REPL[35]:1
```

---------

Co-authored-by: matthias314 <[email protected]>
Co-authored-by: Shuhei Kadowaki <[email protected]>
  • Loading branch information
3 people authored Nov 28, 2023
1 parent df40bab commit 68b4587
Showing 1 changed file with 1 addition and 5 deletions.
6 changes: 1 addition & 5 deletions base/iterators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1559,15 +1559,11 @@ Stacktrace:
return ret
end

# Collections of known size
only(x::Ref) = x[]
only(x::Number) = x
only(x::Char) = x
# Specific error messages for tuples and named tuples
only(x::Tuple{Any}) = x[1]
only(x::Tuple) = throw(
ArgumentError("Tuple contains $(length(x)) elements, must contain exactly 1 element")
)
only(a::AbstractArray{<:Any, 0}) = @inbounds return a[]
only(x::NamedTuple{<:Any, <:Tuple{Any}}) = first(x)
only(x::NamedTuple) = throw(
ArgumentError("NamedTuple contains $(length(x)) elements, must contain exactly 1 element")
Expand Down

0 comments on commit 68b4587

Please sign in to comment.