From 94c3a153c84b4023e7c36556d3971bd165ebe81b Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 15 Sep 2022 01:24:11 -0400 Subject: [PATCH] Improve nothrow analysis of :new with missing sparam (#46754) Similar to #46693, but for :new, rather than getfield. Unfortunately, this is somewhat limited as we don't really have the ability to encode type equality constraints in the lattice. In particular, it would be nice to analyze: ``` struct Foo{T} x::T Foo(x::T) where {T} = new{T}(x) end ``` and be able to prove this nothrow. If it's really important, we could probably pattern match it, but for the moment, this is not easy to do. Nevertheless, we can do something about the similar, but simpler pattern ``` struct Foo{T} x Foo(x::T) where {T} = new{T}(x) end ``` which is what this PR does. --- base/compiler/optimize.jl | 23 +++++++++++++++++++---- test/compiler/effects.jl | 10 ++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 1b6d19bc152b6..be1f653d744bd 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -254,16 +254,31 @@ function stmt_effect_flags(lattice::AbstractLattice, @nospecialize(stmt), @nospe nothrow = is_nothrow(effects) return (consistent, effect_free & nothrow, nothrow) elseif head === :new - typ = argextype(args[1], src) + atyp = argextype(args[1], src) # `Expr(:new)` of unknown type could raise arbitrary TypeError. - typ, isexact = instanceof_tfunc(typ) - isexact || return (false, false, false) - isconcretedispatch(typ) || return (false, false, false) + typ, isexact = instanceof_tfunc(atyp) + if !isexact + atyp = unwrap_unionall(widenconst(atyp)) + if isType(atyp) && isTypeDataType(atyp.parameters[1]) + typ = atyp.parameters[1] + else + return (false, false, false) + end + isabstracttype(typ) && return (false, false, false) + else + isconcretedispatch(typ) || return (false, false, false) + end typ = typ::DataType fieldcount(typ) >= length(args) - 1 || return (false, false, false) for fld_idx in 1:(length(args) - 1) eT = argextype(args[fld_idx + 1], src) fT = fieldtype(typ, fld_idx) + # Currently, we cannot represent any type equality constraints + # in the lattice, so if we see any type of type parameter, + # there is very little we can say about it + if !isexact && has_free_typevars(fT) + return (false, false, false) + end eT ⊑ₒ fT || return (false, false, false) end return (false, true, true) diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index fa0ed3e13947a..eca6e3ca8b1d8 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -655,3 +655,13 @@ end # @testset "effects analysis on array ops" begin # Test that builtin_effects handles vararg correctly @test !Core.Compiler.is_nothrow(Core.Compiler.builtin_effects(Core.Compiler.fallback_lattice, Core.isdefined, Any[String, Vararg{Any}], Bool)) + +# Test that :new can be eliminated even if an sparam is unknown +struct SparamUnused{T} + x + SparamUnused(x::T) where {T} = new{T}(x) +end +mksparamunused(x) = (SparamUnused(x); nothing) +let src = code_typed1(mksparamunused, (Any,)) + @test count(isnew, src.code) == 0 +end