From 6522370b14b2bb56e4da3e06995dc5890f07f78a Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Tue, 29 Oct 2019 00:35:19 -0400 Subject: [PATCH] Add lazy string type --- NEWS.md | 1 + base/Base.jl | 4 ++ base/abstractarray.jl | 34 +++++++------ base/compiler/compiler.jl | 2 + base/exports.jl | 2 + base/math.jl | 11 +++-- base/strings/lazy.jl | 63 ++++++++++++++++++++++++ stdlib/LinearAlgebra/src/blas.jl | 70 +++++++++++++-------------- stdlib/LinearAlgebra/src/matmul.jl | 30 ++++++------ stdlib/LinearAlgebra/src/transpose.jl | 4 +- test/strings/basic.jl | 7 +++ 11 files changed, 157 insertions(+), 71 deletions(-) create mode 100644 base/strings/lazy.jl diff --git a/NEWS.md b/NEWS.md index 98baf83ad1c1e..a720899bb08d0 100644 --- a/NEWS.md +++ b/NEWS.md @@ -102,6 +102,7 @@ Standard library changes * `extrema` now supports `init` keyword argument ([#36265], [#43604]). * Intersect returns a result with the eltype of the type-promoted eltypes of the two inputs ([#41769]). * `Iterators.countfrom` now accepts any type that defines `+`. ([#37747]) +* The `LazyString` and the `lazy"str"` macro were added to support delayed construction of error messages in error paths. ([#33711]) #### InteractiveUtils * A new macro `@time_imports` for reporting any time spent importing packages and their dependencies ([#41612]) diff --git a/base/Base.jl b/base/Base.jl index bccd8e4e25e78..8fd0780133818 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -123,6 +123,9 @@ include("refpointer.jl") include("checked.jl") using .Checked +# Lazy strings +include("strings/lazy.jl") + # array structures include("indices.jl") include("array.jl") @@ -200,6 +203,7 @@ include("dict.jl") include("abstractset.jl") include("set.jl") +# Strings include("char.jl") include("strings/basic.jl") include("strings/string.jl") diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 2733b52222e37..98ac697ab4d12 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -914,19 +914,21 @@ end # copy from an some iterable object into an AbstractArray function copyto!(dest::AbstractArray, dstart::Integer, src, sstart::Integer) if (sstart < 1) - throw(ArgumentError(string("source start offset (",sstart,") is < 1"))) + throw(ArgumentError(LazyString("source start offset (",sstart,") is < 1"))) end y = iterate(src) for j = 1:(sstart-1) if y === nothing - throw(ArgumentError(string("source has fewer elements than required, ", - "expected at least ",sstart,", got ",j-1))) + throw(ArgumentError(LazyString( + "source has fewer elements than required, ", + "expected at least ", sstart,", got ", j-1))) end y = iterate(src, y[2]) end if y === nothing - throw(ArgumentError(string("source has fewer elements than required, ", - "expected at least ",sstart,", got ",sstart-1))) + throw(ArgumentError(LazyString( + "source has fewer elements than required, ", + "expected at least ",sstart," got ", sstart-1))) end i = Int(dstart) while y !== nothing @@ -940,19 +942,22 @@ end # this method must be separate from the above since src might not have a length function copyto!(dest::AbstractArray, dstart::Integer, src, sstart::Integer, n::Integer) - n < 0 && throw(ArgumentError(string("tried to copy n=", n, " elements, but n should be nonnegative"))) + n < 0 && throw(ArgumentError(LazyString("tried to copy n=",n, + ", elements, but n should be nonnegative"))) n == 0 && return dest dmax = dstart + n - 1 inds = LinearIndices(dest) if (dstart ∉ inds || dmax ∉ inds) | (sstart < 1) - sstart < 1 && throw(ArgumentError(string("source start offset (",sstart,") is < 1"))) + sstart < 1 && throw(ArgumentError(LazyString("source start offset (", + sstart,") is < 1"))) throw(BoundsError(dest, dstart:dmax)) end y = iterate(src) for j = 1:(sstart-1) if y === nothing - throw(ArgumentError(string("source has fewer elements than required, ", - "expected at least ",sstart,", got ",j-1))) + throw(ArgumentError(LazyString( + "source has fewer elements than required, ", + "expected at least ",sstart,", got ",j-1))) end y = iterate(src, y[2]) end @@ -1064,7 +1069,8 @@ function copyto!(dest::AbstractArray, dstart::Integer, src::AbstractArray, sstart::Integer, n::Integer) n == 0 && return dest - n < 0 && throw(ArgumentError(string("tried to copy n=", n, " elements, but n should be nonnegative"))) + n < 0 && throw(ArgumentError(LazyString("tried to copy n=", + n," elements, but n should be nonnegative"))) destinds, srcinds = LinearIndices(dest), LinearIndices(src) (checkbounds(Bool, destinds, dstart) && checkbounds(Bool, destinds, dstart+n-1)) || throw(BoundsError(dest, dstart:dstart+n-1)) (checkbounds(Bool, srcinds, sstart) && checkbounds(Bool, srcinds, sstart+n-1)) || throw(BoundsError(src, sstart:sstart+n-1)) @@ -1082,12 +1088,12 @@ end function copyto!(B::AbstractVecOrMat{R}, ir_dest::AbstractRange{Int}, jr_dest::AbstractRange{Int}, A::AbstractVecOrMat{S}, ir_src::AbstractRange{Int}, jr_src::AbstractRange{Int}) where {R,S} if length(ir_dest) != length(ir_src) - throw(ArgumentError(string("source and destination must have same size (got ", - length(ir_src)," and ",length(ir_dest),")"))) + throw(ArgumentError(LazyString("source and destination must have same size (got ", + length(ir_src)," and ",length(ir_dest),")"))) end if length(jr_dest) != length(jr_src) - throw(ArgumentError(string("source and destination must have same size (got ", - length(jr_src)," and ",length(jr_dest),")"))) + throw(ArgumentError(LazyString("source and destination must have same size (got ", + length(jr_src)," and ",length(jr_dest),")"))) end @boundscheck checkbounds(B, ir_dest, jr_dest) @boundscheck checkbounds(A, ir_src, jr_src) diff --git a/base/compiler/compiler.jl b/base/compiler/compiler.jl index aafd80c1127ff..5f2f5614ba209 100644 --- a/base/compiler/compiler.jl +++ b/base/compiler/compiler.jl @@ -67,6 +67,8 @@ add_with_overflow(x::T, y::T) where {T<:SignedInt} = checked_sadd_int(x, y) add_with_overflow(x::T, y::T) where {T<:UnsignedInt} = checked_uadd_int(x, y) add_with_overflow(x::Bool, y::Bool) = (x+y, false) +include("strings/lazy.jl") + # core array operations include("indices.jl") include("array.jl") diff --git a/base/exports.jl b/base/exports.jl index 4d6ceef6a13c0..c43e66eecb74c 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -57,6 +57,7 @@ export IOStream, LinRange, Irrational, + LazyString, Matrix, MergeSort, Missing, @@ -986,6 +987,7 @@ export @v_str, # version number @raw_str, # raw string with no interpolation/unescaping @NamedTuple, + @lazy_str, # lazy string # documentation @text_str, diff --git a/base/math.jl b/base/math.jl index 056574dd02570..1206ac87bb254 100644 --- a/base/math.jl +++ b/base/math.jl @@ -30,13 +30,14 @@ using Core.Intrinsics: sqrt_llvm using .Base: IEEEFloat @noinline function throw_complex_domainerror(f::Symbol, x) - throw(DomainError(x, string("$f will only return a complex result if called with a ", - "complex argument. Try $f(Complex(x))."))) + throw(DomainError(x, + LazyString(f," will only return a complex result if called with a complex argument. Try ", f,"(Complex(x))."))) end @noinline function throw_exp_domainerror(x) - throw(DomainError(x, string("Exponentiation yielding a complex result requires a ", - "complex argument.\nReplace x^y with (x+0im)^y, ", - "Complex(x)^y, or similar."))) + throw(DomainError(x, LazyString( + "Exponentiation yielding a complex result requires a ", + "complex argument.\nReplace x^y with (x+0im)^y, ", + "Complex(x)^y, or similar."))) end # non-type specific math functions diff --git a/base/strings/lazy.jl b/base/strings/lazy.jl new file mode 100644 index 0000000000000..b40fd9a5842b3 --- /dev/null +++ b/base/strings/lazy.jl @@ -0,0 +1,63 @@ +""" + LazyString <: AbstractString + +A lazy representation of string interpolation. This is useful when a string +needs to be constructed in a context where performing the actual interpolation +and string construction is unnecessary or undesirable (e.g. in error paths +of functions). + +This type is designed to be cheap to construct at runtime, trying to offload +as much work as possible to either the macro or later printing operations. + +!!! compat "Julia 1.8" + `LazyString` requires Julia 1.8 or later. +""" +mutable struct LazyString <: AbstractString + parts::Tuple + # Created on first access + str::String + LazyString(args...) = new(args) +end + +""" + lazy"str" + +Create a [`LazyString`](@ref) using regular string interpolation syntax. +Note that interpolations are *evaluated* at LazyString construction time, +but *printing* is delayed until the first access to the string. + +!!! compat "Julia 1.8" + `lazy"str"` requires Julia 1.8 or later. +""" +macro lazy_str(text) + parts = Any[] + lastidx = idx = 1 + while (idx = findnext('$', text, idx)) !== nothing + lastidx < idx && push!(parts, text[lastidx:idx-1]) + idx += 1 + expr, idx = Meta.parseatom(text, idx; filename=string(__source__.file)) + push!(parts, esc(expr)) + lastidx = idx + end + lastidx <= lastindex(text) && push!(parts, text[lastidx:end]) + :(LazyString($(parts...))) +end + +function String(l::LazyString) + if !isdefined(l, :str) + l.str = sprint() do io + for p in l.parts + print(io, p) + end + end + end + return l.str +end + +hash(s::LazyString, h::UInt64) = hash(String(s), h) +lastindex(s::LazyString) = lastindex(String(s)) +iterate(s::LazyString) = iterate(String(s)) +iterate(s::LazyString, i::Integer) = iterate(String(s), i) +isequal(a::LazyString, b::LazyString) = isequal(String(a), String(b)) +==(a::LazyString, b::LazyString) = (String(a) == String(b)) +ncodeunits(s::LazyString) = ncodeunits(String(s)) diff --git a/stdlib/LinearAlgebra/src/blas.jl b/stdlib/LinearAlgebra/src/blas.jl index 19edc52cfff17..b74e03dce2874 100644 --- a/stdlib/LinearAlgebra/src/blas.jl +++ b/stdlib/LinearAlgebra/src/blas.jl @@ -409,7 +409,7 @@ function dot(DX::Union{DenseArray{T},AbstractVector{T}}, DY::Union{DenseArray{T} require_one_based_indexing(DX, DY) n = length(DX) if n != length(DY) - throw(DimensionMismatch("dot product arguments have lengths $(length(DX)) and $(length(DY))")) + throw(DimensionMismatch(lazy"dot product arguments have lengths $(length(DX)) and $(length(DY))")) end return dot(n, DX, stride(DX, 1), DY, stride(DY, 1)) end @@ -417,7 +417,7 @@ function dotc(DX::Union{DenseArray{T},AbstractVector{T}}, DY::Union{DenseArray{T require_one_based_indexing(DX, DY) n = length(DX) if n != length(DY) - throw(DimensionMismatch("dot product arguments have lengths $(length(DX)) and $(length(DY))")) + throw(DimensionMismatch(lazy"dot product arguments have lengths $(length(DX)) and $(length(DY))")) end return dotc(n, DX, stride(DX, 1), DY, stride(DY, 1)) end @@ -425,7 +425,7 @@ function dotu(DX::Union{DenseArray{T},AbstractVector{T}}, DY::Union{DenseArray{T require_one_based_indexing(DX, DY) n = length(DX) if n != length(DY) - throw(DimensionMismatch("dot product arguments have lengths $(length(DX)) and $(length(DY))")) + throw(DimensionMismatch(lazy"dot product arguments have lengths $(length(DX)) and $(length(DY))")) end return dotu(n, DX, stride(DX, 1), DY, stride(DY, 1)) end @@ -544,7 +544,7 @@ for (fname, elty) in ((:daxpy_,:Float64), end function axpy!(alpha::Number, x::Union{DenseArray{T},StridedVector{T}}, y::Union{DenseArray{T},StridedVector{T}}) where T<:BlasFloat if length(x) != length(y) - throw(DimensionMismatch("x has length $(length(x)), but y has length $(length(y))")) + throw(DimensionMismatch(lazy"x has length $(length(x)), but y has length $(length(y))")) end return axpy!(length(x), convert(T,alpha), x, stride(x, 1), y, stride(y, 1)) end @@ -555,10 +555,10 @@ function axpy!(alpha::Number, x::Array{T}, rx::Union{UnitRange{Ti},AbstractRange throw(DimensionMismatch("ranges of differing lengths")) end if minimum(rx) < 1 || maximum(rx) > length(x) - throw(ArgumentError("range out of bounds for x, of length $(length(x))")) + throw(ArgumentError(lazy"range out of bounds for x, of length $(length(x))")) end if minimum(ry) < 1 || maximum(ry) > length(y) - throw(ArgumentError("range out of bounds for y, of length $(length(y))")) + throw(ArgumentError(lazy"range out of bounds for y, of length $(length(y))")) end GC.@preserve x y axpy!( length(rx), @@ -615,7 +615,7 @@ end function axpby!(alpha::Number, x::Union{DenseArray{T},AbstractVector{T}}, beta::Number, y::Union{DenseArray{T},AbstractVector{T}}) where T<:BlasFloat require_one_based_indexing(x, y) if length(x) != length(y) - throw(DimensionMismatch("x has length $(length(x)), but y has length $(length(y))")) + throw(DimensionMismatch(lazy"x has length $(length(x)), but y has length $(length(y))")) end return axpby!(length(x), convert(T, alpha), x, stride(x, 1), convert(T, beta), y, stride(y, 1)) end @@ -665,11 +665,11 @@ for (fname, elty) in ((:dgemv_,:Float64), require_one_based_indexing(A, X, Y) m,n = size(A,1),size(A,2) if trans == 'N' && (length(X) != n || length(Y) != m) - throw(DimensionMismatch("A has dimensions $(size(A)), X has length $(length(X)) and Y has length $(length(Y))")) + throw(DimensionMismatch(lazy"A has dimensions $(size(A)), X has length $(length(X)) and Y has length $(length(Y))")) elseif trans == 'C' && (length(X) != m || length(Y) != n) - throw(DimensionMismatch("the adjoint of A has dimensions $n, $m, X has length $(length(X)) and Y has length $(length(Y))")) + throw(DimensionMismatch(lazy"the adjoint of A has dimensions $n, $m, X has length $(length(X)) and Y has length $(length(Y))")) elseif trans == 'T' && (length(X) != m || length(Y) != n) - throw(DimensionMismatch("the transpose of A has dimensions $n, $m, X has length $(length(X)) and Y has length $(length(Y))")) + throw(DimensionMismatch(lazy"the transpose of A has dimensions $n, $m, X has length $(length(X)) and Y has length $(length(Y))")) end chkstride1(A) lda = stride(A,2) @@ -810,13 +810,13 @@ for (fname, elty, lib) in ((:dsymv_,:Float64,libblastrampoline), require_one_based_indexing(A, x, y) m, n = size(A) if m != n - throw(DimensionMismatch("matrix A is $m by $n but must be square")) + throw(DimensionMismatch(lazy"matrix A is $m by $n but must be square")) end if n != length(x) - throw(DimensionMismatch("A has size $(size(A)), and x has length $(length(x))")) + throw(DimensionMismatch(lazy"A has size $(size(A)), and x has length $(length(x))")) end if m != length(y) - throw(DimensionMismatch("A has size $(size(A)), and y has length $(length(y))")) + throw(DimensionMismatch(lazy"A has size $(size(A)), and y has length $(length(y))")) end chkstride1(A) ccall((@blasfunc($fname), $lib), Cvoid, @@ -872,13 +872,13 @@ for (fname, elty) in ((:zhemv_,:ComplexF64), require_one_based_indexing(A, x, y) m, n = size(A) if m != n - throw(DimensionMismatch("matrix A is $m by $n but must be square")) + throw(DimensionMismatch(lazy"matrix A is $m by $n but must be square")) end if n != length(x) - throw(DimensionMismatch("A has size $(size(A)), and x has length $(length(x))")) + throw(DimensionMismatch(lazy"A has size $(size(A)), and x has length $(length(x))")) end if m != length(y) - throw(DimensionMismatch("A has size $(size(A)), and y has length $(length(y))")) + throw(DimensionMismatch(lazy"A has size $(size(A)), and y has length $(length(y))")) end chkstride1(A) lda = max(1, stride(A, 2)) @@ -1300,7 +1300,7 @@ for (fname, elty) in ((:dtrmv_,:Float64), require_one_based_indexing(A, x) n = checksquare(A) if n != length(x) - throw(DimensionMismatch("A has size ($n,$n), x has length $(length(x))")) + throw(DimensionMismatch(lazy"A has size ($n,$n), x has length $(length(x))")) end chkstride1(A) ccall((@blasfunc($fname), libblastrampoline), Cvoid, @@ -1356,7 +1356,7 @@ for (fname, elty) in ((:dtrsv_,:Float64), require_one_based_indexing(A, x) n = checksquare(A) if n != length(x) - throw(DimensionMismatch("size of A is $n != length(x) = $(length(x))")) + throw(DimensionMismatch(lazy"size of A is $n != length(x) = $(length(x))")) end chkstride1(A) ccall((@blasfunc($fname), libblastrampoline), Cvoid, @@ -1391,7 +1391,7 @@ for (fname, elty) in ((:dger_,:Float64), require_one_based_indexing(A, x, y) m, n = size(A) if m != length(x) || n != length(y) - throw(DimensionMismatch("A has size ($m,$n), x has length $(length(x)), y has length $(length(y))")) + throw(DimensionMismatch(lazy"A has size ($m,$n), x has length $(length(x)), y has length $(length(y))")) end ccall((@blasfunc($fname), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, @@ -1425,7 +1425,7 @@ for (fname, elty, lib) in ((:dsyr_,:Float64,libblastrampoline), require_one_based_indexing(A, x) n = checksquare(A) if length(x) != n - throw(DimensionMismatch("A has size ($n,$n), x has length $(length(x))")) + throw(DimensionMismatch(lazy"A has size ($n,$n), x has length $(length(x))")) end ccall((@blasfunc($fname), $lib), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, @@ -1456,7 +1456,7 @@ for (fname, elty, relty) in ((:zher_,:ComplexF64, :Float64), require_one_based_indexing(A, x) n = checksquare(A) if length(x) != n - throw(DimensionMismatch("A has size ($n,$n), x has length $(length(x))")) + throw(DimensionMismatch(lazy"A has size ($n,$n), x has length $(length(x))")) end ccall((@blasfunc($fname), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{$relty}, Ptr{$elty}, @@ -1506,7 +1506,7 @@ for (gemm, elty) in kb = size(B, transB == 'N' ? 1 : 2) n = size(B, transB == 'N' ? 2 : 1) if ka != kb || m != size(C,1) || n != size(C,2) - throw(DimensionMismatch("A has size ($m,$ka), B has size ($kb,$n), C has size $(size(C))")) + throw(DimensionMismatch(lazy"A has size ($m,$ka), B has size ($kb,$n), C has size $(size(C))")) end chkstride1(A) chkstride1(B) @@ -1567,10 +1567,10 @@ for (mfname, elty) in ((:dsymm_,:Float64), m, n = size(C) j = checksquare(A) if j != (side == 'L' ? m : n) - throw(DimensionMismatch("A has size $(size(A)), C has size ($m,$n)")) + throw(DimensionMismatch(lazy"A has size $(size(A)), C has size ($m,$n)")) end if size(B,2) != n - throw(DimensionMismatch("B has second dimension $(size(B,2)) but needs to match second dimension of C, $n")) + throw(DimensionMismatch(lazy"B has second dimension $(size(B,2)) but needs to match second dimension of C, $n")) end chkstride1(A) chkstride1(B) @@ -1641,10 +1641,10 @@ for (mfname, elty) in ((:zhemm_,:ComplexF64), m, n = size(C) j = checksquare(A) if j != (side == 'L' ? m : n) - throw(DimensionMismatch("A has size $(size(A)), C has size ($m,$n)")) + throw(DimensionMismatch(lazy"A has size $(size(A)), C has size ($m,$n)")) end if size(B,2) != n - throw(DimensionMismatch("B has second dimension $(size(B,2)) but needs to match second dimension of C, $n")) + throw(DimensionMismatch(lazy"B has second dimension $(size(B,2)) but needs to match second dimension of C, $n")) end chkstride1(A) chkstride1(B) @@ -1735,7 +1735,7 @@ for (fname, elty) in ((:dsyrk_,:Float64), require_one_based_indexing(A, C) n = checksquare(C) nn = size(A, trans == 'N' ? 1 : 2) - if nn != n throw(DimensionMismatch("C has size ($n,$n), corresponding dimension of A is $nn")) end + if nn != n throw(DimensionMismatch(lazy"C has size ($n,$n), corresponding dimension of A is $nn")) end k = size(A, trans == 'N' ? 2 : 1) chkstride1(A) chkstride1(C) @@ -1793,7 +1793,7 @@ for (fname, elty, relty) in ((:zherk_, :ComplexF64, :Float64), n = checksquare(C) nn = size(A, trans == 'N' ? 1 : 2) if nn != n - throw(DimensionMismatch("the matrix to update has dimension $n but the implied dimension of the update is $(size(A, trans == 'N' ? 1 : 2))")) + throw(DimensionMismatch(lazy"the matrix to update has dimension $n but the implied dimension of the update is $(size(A, trans == 'N' ? 1 : 2))")) end chkstride1(A) chkstride1(C) @@ -1837,7 +1837,7 @@ for (fname, elty) in ((:dsyr2k_,:Float64), require_one_based_indexing(A, B, C) n = checksquare(C) nn = size(A, trans == 'N' ? 1 : 2) - if nn != n throw(DimensionMismatch("C has size ($n,$n), corresponding dimension of A is $nn")) end + if nn != n throw(DimensionMismatch(lazy"C has size ($n,$n), corresponding dimension of A is $nn")) end k = size(A, trans == 'N' ? 2 : 1) chkstride1(A) chkstride1(B) @@ -1905,7 +1905,7 @@ for (fname, elty1, elty2) in ((:zher2k_,:ComplexF64,:Float64), (:cher2k_,:Comple require_one_based_indexing(A, B, C) n = checksquare(C) nn = size(A, trans == 'N' ? 1 : 2) - if nn != n throw(DimensionMismatch("C has size ($n,$n), corresponding dimension of A is $nn")) end + if nn != n throw(DimensionMismatch(lazy"C has size ($n,$n), corresponding dimension of A is $nn")) end chkstride1(A) chkstride1(B) chkstride1(C) @@ -2022,7 +2022,7 @@ for (mmname, smname, elty) in m, n = size(B) nA = checksquare(A) if nA != (side == 'L' ? m : n) - throw(DimensionMismatch("size of A, $(size(A)), doesn't match $side size of B with dims, $(size(B))")) + throw(DimensionMismatch(lazy"size of A, $(size(A)), doesn't match $side size of B with dims, $(size(B))")) end chkstride1(A) chkstride1(B) @@ -2053,7 +2053,7 @@ for (mmname, smname, elty) in m, n = size(B) k = checksquare(A) if k != (side == 'L' ? m : n) - throw(DimensionMismatch("size of A is ($k,$k), size of B is ($m,$n), side is $side, and transa='$transa'")) + throw(DimensionMismatch(lazy"size of A is ($k,$k), size of B is ($m,$n), side is $side, and transa='$transa'")) end chkstride1(A) chkstride1(B) @@ -2079,13 +2079,13 @@ end # module function copyto!(dest::Array{T}, rdest::Union{UnitRange{Ti},AbstractRange{Ti}}, src::Array{T}, rsrc::Union{UnitRange{Ti},AbstractRange{Ti}}) where {T<:BlasFloat,Ti<:Integer} if minimum(rdest) < 1 || maximum(rdest) > length(dest) - throw(ArgumentError("range out of bounds for dest, of length $(length(dest))")) + throw(ArgumentError(lazy"range out of bounds for dest, of length $(length(dest))")) end if minimum(rsrc) < 1 || maximum(rsrc) > length(src) - throw(ArgumentError("range out of bounds for src, of length $(length(src))")) + throw(ArgumentError(lazy"range out of bounds for src, of length $(length(src))")) end if length(rdest) != length(rsrc) - throw(DimensionMismatch("ranges must be of the same length")) + throw(DimensionMismatch(lazy"ranges must be of the same length")) end GC.@preserve src dest BLAS.blascopy!( length(rsrc), diff --git a/stdlib/LinearAlgebra/src/matmul.jl b/stdlib/LinearAlgebra/src/matmul.jl index 297386a3bfc94..9338b5e10d73d 100644 --- a/stdlib/LinearAlgebra/src/matmul.jl +++ b/stdlib/LinearAlgebra/src/matmul.jl @@ -11,7 +11,7 @@ dot(x::Union{DenseArray{T},StridedVector{T}}, y::Union{DenseArray{T},StridedVect function dot(x::Vector{T}, rx::AbstractRange{TI}, y::Vector{T}, ry::AbstractRange{TI}) where {T<:BlasReal,TI<:Integer} if length(rx) != length(ry) - throw(DimensionMismatch("length of rx, $(length(rx)), does not equal length of ry, $(length(ry))")) + throw(DimensionMismatch(lazy"length of rx, $(length(rx)), does not equal length of ry, $(length(ry))")) end if minimum(rx) < 1 || maximum(rx) > length(x) throw(BoundsError(x, rx)) @@ -24,7 +24,7 @@ end function dot(x::Vector{T}, rx::AbstractRange{TI}, y::Vector{T}, ry::AbstractRange{TI}) where {T<:BlasComplex,TI<:Integer} if length(rx) != length(ry) - throw(DimensionMismatch("length of rx, $(length(rx)), does not equal length of ry, $(length(ry))")) + throw(DimensionMismatch(lazy"length of rx, $(length(rx)), does not equal length of ry, $(length(ry))")) end if minimum(rx) < 1 || maximum(rx) > length(x) throw(BoundsError(x, rx)) @@ -496,7 +496,7 @@ end A[i,j] = conjugate ? adjoint(A[j,i]) : transpose(A[j,i]) end else - throw(ArgumentError("uplo argument must be 'U' (upper) or 'L' (lower), got $uplo")) + throw(ArgumentError(lazy"uplo argument must be 'U' (upper) or 'L' (lower), got $uplo")) end A end @@ -505,10 +505,10 @@ function gemv!(y::StridedVector{T}, tA::AbstractChar, A::StridedVecOrMat{T}, x:: α::Number=true, β::Number=false) where {T<:BlasFloat} mA, nA = lapack_size(tA, A) if nA != length(x) - throw(DimensionMismatch("second dimension of A, $nA, does not match length of x, $(length(x))")) + throw(DimensionMismatch(lazy"second dimension of A, $nA, does not match length of x, $(length(x))")) end if mA != length(y) - throw(DimensionMismatch("first dimension of A, $mA, does not match length of y, $(length(y))")) + throw(DimensionMismatch(lazy"first dimension of A, $mA, does not match length of y, $(length(y))")) end if mA == 0 return y @@ -536,7 +536,7 @@ function syrk_wrapper!(C::StridedMatrix{T}, tA::AbstractChar, A::StridedVecOrMat tAt = 'T' end if nC != mA - throw(DimensionMismatch("output matrix has size: $(nC), but should have size $(mA)")) + throw(DimensionMismatch(lazy"output matrix has size: $(nC), but should have size $(mA)")) end if mA == 0 || nA == 0 || iszero(_add.alpha) return _rmul_or_fill!(C, _add.beta) @@ -574,7 +574,7 @@ function herk_wrapper!(C::Union{StridedMatrix{T}, StridedMatrix{Complex{T}}}, tA tAt = 'C' end if nC != mA - throw(DimensionMismatch("output matrix has size: $(nC), but should have size $(mA)")) + throw(DimensionMismatch(lazy"output matrix has size: $(nC), but should have size $(mA)")) end if mA == 0 || nA == 0 || iszero(_add.alpha) return _rmul_or_fill!(C, _add.beta) @@ -618,7 +618,7 @@ function gemm_wrapper!(C::StridedVecOrMat{T}, tA::AbstractChar, tB::AbstractChar mB, nB = lapack_size(tB, B) if nA != mB - throw(DimensionMismatch("A has dimensions ($mA,$nA) but B has dimensions ($mB,$nB)")) + throw(DimensionMismatch(lazy"A has dimensions ($mA,$nA) but B has dimensions ($mB,$nB)")) end if C === A || B === C @@ -627,7 +627,7 @@ function gemm_wrapper!(C::StridedVecOrMat{T}, tA::AbstractChar, tB::AbstractChar if mA == 0 || nA == 0 || nB == 0 || iszero(_add.alpha) if size(C) != (mA, nB) - throw(DimensionMismatch("C has dimensions $(size(C)), should have ($mA,$nB)")) + throw(DimensionMismatch(lazy"C has dimensions $(size(C)), should have ($mA,$nB)")) end return _rmul_or_fill!(C, _add.beta) end @@ -688,10 +688,10 @@ function generic_matvecmul!(C::AbstractVector{R}, tA, A::AbstractVecOrMat, B::Ab mB = length(B) mA, nA = lapack_size(tA, A) if mB != nA - throw(DimensionMismatch("matrix A has dimensions ($mA,$nA), vector B has length $mB")) + throw(DimensionMismatch(lazy"matrix A has dimensions ($mA,$nA), vector B has length $mB")) end if mA != length(C) - throw(DimensionMismatch("result C has length $(length(C)), needs length $mA")) + throw(DimensionMismatch(lazy"result C has length $(length(C)), needs length $mA")) end Astride = size(A, 1) @@ -785,10 +785,10 @@ function _generic_matmatmul!(C::AbstractVecOrMat{R}, tA, tB, A::AbstractVecOrMat mA, nA = lapack_size(tA, A) mB, nB = lapack_size(tB, B) if mB != nA - throw(DimensionMismatch("matrix A has dimensions ($mA,$nA), matrix B has dimensions ($mB,$nB)")) + throw(DimensionMismatch(lazy"matrix A has dimensions ($mA,$nA), matrix B has dimensions ($mB,$nB)")) end if size(C,1) != mA || size(C,2) != nB - throw(DimensionMismatch("result C has dimensions $(size(C)), needs ($mA,$nB)")) + throw(DimensionMismatch(lazy"result C has dimensions $(size(C)), needs ($mA,$nB)")) end if iszero(_add.alpha) || isempty(A) || isempty(B) @@ -961,7 +961,7 @@ function matmul2x2!(C::AbstractMatrix, tA, tB, A::AbstractMatrix, B::AbstractMat _add::MulAddMul = MulAddMul()) require_one_based_indexing(C, A, B) if !(size(A) == size(B) == size(C) == (2,2)) - throw(DimensionMismatch("A has size $(size(A)), B has size $(size(B)), C has size $(size(C))")) + throw(DimensionMismatch(lazy"A has size $(size(A)), B has size $(size(B)), C has size $(size(C))")) end @inbounds begin if tA == 'T' @@ -1004,7 +1004,7 @@ function matmul3x3!(C::AbstractMatrix, tA, tB, A::AbstractMatrix, B::AbstractMat _add::MulAddMul = MulAddMul()) require_one_based_indexing(C, A, B) if !(size(A) == size(B) == size(C) == (3,3)) - throw(DimensionMismatch("A has size $(size(A)), B has size $(size(B)), C has size $(size(C))")) + throw(DimensionMismatch(lazy"A has size $(size(A)), B has size $(size(B)), C has size $(size(C))")) end @inbounds begin if tA == 'T' diff --git a/stdlib/LinearAlgebra/src/transpose.jl b/stdlib/LinearAlgebra/src/transpose.jl index 2734789b751b0..c7ca6339aac6a 100644 --- a/stdlib/LinearAlgebra/src/transpose.jl +++ b/stdlib/LinearAlgebra/src/transpose.jl @@ -181,11 +181,11 @@ Base.copy(A::Adjoint{<:Any,<:AbstractMatrix}) = adjoint!(similar(A.parent, rever function copy_transpose!(B::AbstractVecOrMat, ir_dest::AbstractRange{Int}, jr_dest::AbstractRange{Int}, A::AbstractVecOrMat, ir_src::AbstractRange{Int}, jr_src::AbstractRange{Int}) if length(ir_dest) != length(jr_src) - throw(ArgumentError(string("source and destination must have same size (got ", + throw(ArgumentError(LazyString("source and destination must have same size (got ", length(jr_src)," and ",length(ir_dest),")"))) end if length(jr_dest) != length(ir_src) - throw(ArgumentError(string("source and destination must have same size (got ", + throw(ArgumentError(LazyString("source and destination must have same size (got ", length(ir_src)," and ",length(jr_dest),")"))) end @boundscheck checkbounds(B, ir_dest, jr_dest) diff --git a/test/strings/basic.jl b/test/strings/basic.jl index 2bdac1b353e1f..1da897667a2ea 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -1094,3 +1094,10 @@ end @test sprint(summary, SubString("foα", 2)) == "3-codeunit SubString{String}" @test sprint(summary, "") == "empty String" end + +@testset "LazyString" begin + @test repr(lazy"$(1+2) is 3") == "\"3 is 3\"" + let d = Dict(lazy"$(1+2) is 3" => 3) + @test d["3 is 3"] == 3 + end +end \ No newline at end of file