@@ -433,6 +433,75 @@ function const_datatype_getfield_tfunc(sv, fld)
433433 return nothing
434434end
435435
436+ function try_compute_fieldidx (@nospecialize (typ), @nospecialize (field))
437+ if isa (field, Symbol)
438+ field = fieldindex (typ, field, false )
439+ field == 0 && return nothing
440+ elseif isa (field, Integer)
441+ (1 <= field <= fieldcount (typ)) || return nothing
442+ else
443+ return nothing
444+ end
445+ return field
446+ end
447+
448+ function getfield_nothrow (argtypes:: Vector{Any} )
449+ 2 <= length (argtypes) <= 3 || return false
450+ length (argtypes) == 2 && return getfield_nothrow (argtypes[1 ], argtypes[2 ], Const (true ))
451+ return getfield_nothrow (argtypes[1 ], argtypes[2 ], argtypes[3 ])
452+ end
453+ function getfield_nothrow (@nospecialize (s00), @nospecialize (name), @nospecialize (inbounds))
454+ bounds_check_disabled = isa (inbounds, Const) && inbounds. val === false
455+ # If we don't have invounds and don't know the field, don't even bother
456+ if ! bounds_check_disabled
457+ isa (name, Const) || return false
458+ end
459+
460+ # If we have s00 being a const, we can potentially refine our type-based analysis above
461+ if isa (s00, Const) || isconstType (s00)
462+ if ! isa (s00, Const)
463+ sv = s00. parameters[1 ]
464+ else
465+ sv = s00. val
466+ end
467+ if isa (name, Const)
468+ (isa (sv, Module) && isa (name. val, Symbol)) || return false
469+ (isa (name. val, Symbol) || isa (name. val, Int)) || return false
470+ return isdefined (sv, name. val)
471+ end
472+ if bounds_check_disabled && ! isa (sv, Module)
473+ # If bounds checking is disabled and all fields are assigned,
474+ # we may assume that we don't throw
475+ for i = 1 : fieldcount (typeof (sv))
476+ isdefined (sv, i) || return false
477+ end
478+ return true
479+ end
480+ return false
481+ end
482+
483+ s = unwrap_unionall (widenconst (s00))
484+ if isa (s, Union)
485+ return getfield_nothrow (rewrap (s. a, s00), name, inbounds) &&
486+ getfield_nothrow (rewrap (s. b, s00), name, inbounds)
487+ elseif isa (s, Conditional)
488+ return false
489+ elseif isa (s, DataType)
490+ # Can't say anything about abstract types
491+ s. abstract && return false
492+ # If all fields are always initialized, and bounds check is disabled, we can assume
493+ # we don't throw
494+ (fieldcount (s) == s. ninitialized) && return true
495+ # Else we need to know what the field is
496+ isa (name, Const) || return false
497+ field = try_compute_fieldidx (s, name. val)
498+ field === nothing && return false
499+ field <= s. ninitialized && return true
500+ end
501+
502+ return false
503+ end
504+
436505getfield_tfunc (@nospecialize (s00), @nospecialize (name), @nospecialize (inbounds)) =
437506 getfield_tfunc (s00, name)
438507function getfield_tfunc (@nospecialize (s00), @nospecialize (name))
@@ -770,6 +839,45 @@ function tuple_tfunc(@nospecialize(argtype))
770839 return argtype
771840end
772841
842+ function array_builtin_common_nothrow (argtypes, first_idx_idx)
843+ length (argtypes) >= 4 || return false
844+ (argtypes[0 ] ⊑ Bool && argtypes[1 ] ⊑ Array) || return false
845+ for i = first_idx_idx: length (argtypes)
846+ argtypes[i] ⊑ Int || return false
847+ end
848+ # If we have @inbounds (first argument is false), we're allowed to assume we don't throw
849+ (isa (argtypes[0 ], Const) && ! argtypes[0 ]. val) && return true
850+ # Else we can't really say anything here
851+ # TODO : In the future we may be able to track the shapes of arrays though
852+ # inference.
853+ return false
854+ end
855+
856+ # Query whether the given builtin is guaranteed not to throw given the argtypes
857+ function builtin_nothrow (@nospecialize (f), argtypes:: Array{Any,1} )
858+ (f === tuple || f === svec) && return true
859+ if f === arrayset
860+ array_builtin_common_nothrow (argtypes, 4 ) || return true
861+ # Additionally check element type compatibility
862+ a = widenconst (argtypes[2 ])
863+ # Check that we can determine the element type
864+ (isa (a, DataType) && (isa (a. parameters[1 ], Type) || isa (a. parameters[1 ], TypeVar))) || return false
865+ at = a. parameters[1 ]
866+ # Check that the element type is compatible with the element we're assigning
867+ (argtypes[3 ] ⊑ (isa (at, Type) ? at : at. lb)) || return false
868+ return true
869+ elseif f === arrayref
870+ return array_builtin_common_nothrow (argtypes, 3 )
871+ elseif f === Core. _expr
872+ return length (argtypes) >= 1 && argtypes[1 ] ⊑ Symbol
873+ elseif f === invoke
874+ return false
875+ elseif f === getfield
876+ return getfield_nothrow (argtypes)
877+ end
878+ return false
879+ end
880+
773881function builtin_tfunction (@nospecialize (f), argtypes:: Array{Any,1} ,
774882 sv:: Union{InferenceState,Nothing} , params:: Params = sv. params)
775883 isva = ! isempty (argtypes) && isvarargtype (argtypes[end ])
0 commit comments