@@ -236,15 +236,21 @@ function _typeinf(interp::AbstractInterpreter, frame::InferenceState)
236236 # with no active ip's, frame is done
237237 frames = frame. callers_in_cycle
238238 isempty (frames) && push! (frames, frame)
239- valid_worlds = WorldRange ()
239+ cycle_valid_worlds = WorldRange ()
240+ cycle_effects = EFFECTS_TOTAL
240241 for caller in frames
241242 @assert ! (caller. dont_work_on_me)
242243 caller. dont_work_on_me = true
243- # might might not fully intersect these earlier, so do that now
244- valid_worlds = intersect (caller. valid_worlds, valid_worlds)
244+ # converge the world age range and effects for this cycle here:
245+ # all frames in the cycle should have the same bits of `valid_worlds` and `effects`
246+ # that are simply the intersection of each partial computation, without having
247+ # dependencies on each other (unlike rt and exct)
248+ cycle_valid_worlds = intersect (cycle_valid_worlds, caller. valid_worlds)
249+ cycle_effects = merge_effects (cycle_effects, caller. ipo_effects)
245250 end
246251 for caller in frames
247- caller. valid_worlds = valid_worlds
252+ caller. valid_worlds = cycle_valid_worlds
253+ caller. ipo_effects = cycle_effects
248254 finish (caller, caller. interp)
249255 end
250256 for caller in frames
@@ -864,7 +870,8 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize
864870 update_valid_age! (caller, frame. valid_worlds)
865871 isinferred = is_inferred (frame)
866872 edge = isinferred ? mi : nothing
867- effects = isinferred ? frame. result. ipo_effects : adjust_effects (Effects (), method) # effects are adjusted already within `finish` for ipo_effects
873+ effects = isinferred ? frame. result. ipo_effects : # effects are adjusted already within `finish` for ipo_effects
874+ adjust_effects (effects_for_cycle (frame. ipo_effects), method)
868875 exc_bestguess = refine_exception_type (frame. exc_bestguess, effects)
869876 # propagate newly inferred source to the inliner, allowing efficient inlining w/o deserialization:
870877 # note that this result is cached globally exclusively, so we can use this local result destructively
@@ -877,11 +884,16 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize
877884 # return the current knowledge about this cycle
878885 frame = frame:: InferenceState
879886 update_valid_age! (caller, frame. valid_worlds)
880- effects = adjust_effects (Effects ( ), method)
887+ effects = adjust_effects (effects_for_cycle (frame . ipo_effects ), method)
881888 exc_bestguess = refine_exception_type (frame. exc_bestguess, effects)
882889 return EdgeCallResult (frame. bestguess, exc_bestguess, nothing , effects)
883890end
884891
892+ # The `:terminates` effect bit must be conservatively tainted unless recursion cycle has
893+ # been fully resolved. As for other effects, there's no need to taint them at this moment
894+ # because they will be tainted as we try to resolve the cycle.
895+ effects_for_cycle (effects:: Effects ) = Effects (effects; terminates= false )
896+
885897function cached_return_type (code:: CodeInstance )
886898 rettype = code. rettype
887899 isdefined (code, :rettype_const ) || return rettype
0 commit comments