@@ -937,7 +937,7 @@ function getfield_boundscheck((; fargs, argtypes)::ArgInfo) # Symbol
937
937
return :unknown
938
938
end
939
939
940
- function getfield_nothrow (arginfo:: ArgInfo , boundscheck:: Symbol = getfield_boundscheck (arginfo))
940
+ function getfield_nothrow (𝕃 :: AbstractLattice , arginfo:: ArgInfo , boundscheck:: Symbol = getfield_boundscheck (arginfo))
941
941
(;argtypes) = arginfo
942
942
boundscheck === :unknown && return false
943
943
ordering = Const (:not_atomic )
@@ -958,17 +958,19 @@ function getfield_nothrow(arginfo::ArgInfo, boundscheck::Symbol=getfield_boundsc
958
958
if ordering != = :not_atomic # TODO : this is assuming not atomic
959
959
return false
960
960
end
961
- return getfield_nothrow (argtypes[2 ], argtypes[3 ], ! (boundscheck === :off ))
961
+ return getfield_nothrow (𝕃, argtypes[2 ], argtypes[3 ], ! (boundscheck === :off ))
962
962
else
963
963
return false
964
964
end
965
965
end
966
- @nospecs function getfield_nothrow (s00, name, boundscheck:: Bool )
966
+ @nospecs function getfield_nothrow (𝕃 :: AbstractLattice , s00, name, boundscheck:: Bool )
967
967
# If we don't have boundscheck off and don't know the field, don't even bother
968
968
if boundscheck
969
969
isa (name, Const) || return false
970
970
end
971
971
972
+ ⊑ = Core. Compiler.:⊑ (𝕃)
973
+
972
974
# If we have s00 being a const, we can potentially refine our type-based analysis above
973
975
if isa (s00, Const) || isconstType (s00)
974
976
if ! isa (s00, Const)
@@ -984,31 +986,32 @@ end
984
986
end
985
987
return isdefined (sv, nval)
986
988
end
987
- if ! boundscheck && ! isa (sv, Module)
988
- # If bounds checking is disabled and all fields are assigned,
989
- # we may assume that we don't throw
990
- for i = 1 : fieldcount ( typeof (sv))
991
- isdefined (sv, i) || return false
992
- end
993
- return true
989
+ boundscheck && return false
990
+ # If bounds checking is disabled and all fields are assigned,
991
+ # we may assume that we don't throw
992
+ isa (sv, Module) && return false
993
+ name ⊑ Int || name ⊑ Symbol || return false
994
+ for i = 1 : fieldcount ( typeof (sv))
995
+ isdefined (sv, i) || return false
994
996
end
995
- return false
997
+ return true
996
998
end
997
999
998
1000
s0 = widenconst (s00)
999
1001
s = unwrap_unionall (s0)
1000
1002
if isa (s, Union)
1001
- return getfield_nothrow (rewrap_unionall (s. a, s00), name, boundscheck) &&
1002
- getfield_nothrow (rewrap_unionall (s. b, s00), name, boundscheck)
1003
+ return getfield_nothrow (𝕃, rewrap_unionall (s. a, s00), name, boundscheck) &&
1004
+ getfield_nothrow (𝕃, rewrap_unionall (s. b, s00), name, boundscheck)
1003
1005
elseif isType (s) && isTypeDataType (s. parameters[1 ])
1004
1006
s = s0 = DataType
1005
1007
end
1006
1008
if isa (s, DataType)
1007
1009
# Can't say anything about abstract types
1008
1010
isabstracttype (s) && return false
1009
- # If all fields are always initialized, and bounds check is disabled, we can assume
1010
- # we don't throw
1011
+ # If all fields are always initialized, and bounds check is disabled,
1012
+ # we can assume we don't throw
1011
1013
if ! boundscheck && s. name. n_uninitialized == 0
1014
+ name ⊑ Int || name ⊑ Symbol || return false
1012
1015
return true
1013
1016
end
1014
1017
# Else we need to know what the field is
@@ -1999,18 +2002,20 @@ function array_type_undefable(@nospecialize(arytype))
1999
2002
end
2000
2003
end
2001
2004
2002
- function array_builtin_common_nothrow (argtypes:: Vector{Any} , first_idx_idx:: Int )
2005
+ function array_builtin_common_nothrow (argtypes:: Vector{Any} , first_idx_idx:: Int , isarrayref :: Bool )
2003
2006
length (argtypes) >= 4 || return false
2004
2007
boundscheck = argtypes[1 ]
2005
2008
arytype = argtypes[2 ]
2006
2009
array_builtin_common_typecheck (boundscheck, arytype, argtypes, first_idx_idx) || return false
2007
- # If we could potentially throw undef ref errors, bail out now.
2008
- arytype = widenconst (arytype)
2009
- array_type_undefable (arytype) && return false
2010
+ if isarrayref
2011
+ # If we could potentially throw undef ref errors, bail out now.
2012
+ arytype = widenconst (arytype)
2013
+ array_type_undefable (arytype) && return false
2014
+ end
2010
2015
# If we have @inbounds (first argument is false), we're allowed to assume
2011
2016
# we don't throw bounds errors.
2012
2017
if isa (boundscheck, Const)
2013
- ! ( boundscheck. val:: Bool ) && return true
2018
+ boundscheck. val:: Bool || return true
2014
2019
end
2015
2020
# Else we can't really say anything here
2016
2021
# TODO : In the future we may be able to track the shapes of arrays though
@@ -2042,11 +2047,11 @@ end
2042
2047
@nospecs function _builtin_nothrow (𝕃:: AbstractLattice , f, argtypes:: Vector{Any} , rt)
2043
2048
⊑ = Core. Compiler.:⊑ (𝕃)
2044
2049
if f === arrayset
2045
- array_builtin_common_nothrow (argtypes, 4 ) || return false
2050
+ array_builtin_common_nothrow (argtypes, 4 , #= isarrayref =# false ) || return false
2046
2051
# Additionally check element type compatibility
2047
2052
return arrayset_typecheck (argtypes[2 ], argtypes[3 ])
2048
2053
elseif f === arrayref || f === const_arrayref
2049
- return array_builtin_common_nothrow (argtypes, 3 )
2054
+ return array_builtin_common_nothrow (argtypes, 3 , #= isarrayref =# true )
2050
2055
elseif f === Core. _expr
2051
2056
length (argtypes) >= 1 || return false
2052
2057
return argtypes[1 ] ⊑ Symbol
@@ -2065,7 +2070,7 @@ end
2065
2070
elseif f === invoke
2066
2071
return false
2067
2072
elseif f === getfield
2068
- return getfield_nothrow (ArgInfo (nothing , Any[Const (f), argtypes... ]))
2073
+ return getfield_nothrow (𝕃, ArgInfo (nothing , Any[Const (f), argtypes... ]))
2069
2074
elseif f === setfield!
2070
2075
if na == 3
2071
2076
return setfield!_nothrow (𝕃, argtypes[1 ], argtypes[2 ], argtypes[3 ])
@@ -2222,7 +2227,7 @@ function isdefined_effects(𝕃::AbstractLattice, argtypes::Vector{Any})
2222
2227
return Effects (EFFECTS_TOTAL; consistent, nothrow)
2223
2228
end
2224
2229
2225
- function getfield_effects (arginfo:: ArgInfo , @nospecialize (rt))
2230
+ function getfield_effects (𝕃 :: AbstractLattice , arginfo:: ArgInfo , @nospecialize (rt))
2226
2231
(;argtypes) = arginfo
2227
2232
# consistent if the argtype is immutable
2228
2233
length (argtypes) < 3 && return EFFECTS_THROWS
@@ -2238,9 +2243,9 @@ function getfield_effects(arginfo::ArgInfo, @nospecialize(rt))
2238
2243
if ! (length (argtypes) ≥ 3 && getfield_notundefined (obj, argtypes[3 ]))
2239
2244
consistent = ALWAYS_FALSE
2240
2245
end
2241
- nothrow = getfield_nothrow (arginfo, :on )
2246
+ bcheck = getfield_boundscheck (arginfo)
2247
+ nothrow = getfield_nothrow (𝕃, arginfo, bcheck)
2242
2248
if ! nothrow
2243
- bcheck = getfield_boundscheck (arginfo)
2244
2249
if ! (bcheck === :on || bcheck === :boundscheck )
2245
2250
# If we cannot independently prove inboundsness, taint consistency.
2246
2251
# The inbounds-ness assertion requires dynamic reachability, while
@@ -2291,7 +2296,7 @@ function builtin_effects(𝕃::AbstractLattice, @nospecialize(f::Builtin), argin
2291
2296
@assert ! contains_is (_SPECIAL_BUILTINS, f)
2292
2297
2293
2298
if f === getfield
2294
- return getfield_effects (arginfo, rt)
2299
+ return getfield_effects (𝕃, arginfo, rt)
2295
2300
end
2296
2301
argtypes = arginfo. argtypes[2 : end ]
2297
2302
0 commit comments