@@ -245,15 +245,21 @@ function _typeinf(interp::AbstractInterpreter, frame::InferenceState)
245245 # with no active ip's, frame is done
246246 frames = frame. callers_in_cycle
247247 isempty (frames) && push! (frames, frame)
248- valid_worlds = WorldRange ()
248+ cycle_valid_worlds = WorldRange ()
249+ cycle_effects = EFFECTS_TOTAL
249250 for caller in frames
250251 @assert ! (caller. dont_work_on_me)
251252 caller. dont_work_on_me = true
252- # might might not fully intersect these earlier, so do that now
253- valid_worlds = intersect (caller. valid_worlds, valid_worlds)
253+ # converge the world age range and effects for this cycle here:
254+ # all frames in the cycle should have the same bits of `valid_worlds` and `effects`
255+ # that are simply the intersection of each partial computation, without having
256+ # dependencies on each other (unlike rt and exct)
257+ cycle_valid_worlds = intersect (cycle_valid_worlds, caller. valid_worlds)
258+ cycle_effects = merge_effects (cycle_effects, caller. ipo_effects)
254259 end
255260 for caller in frames
256- caller. valid_worlds = valid_worlds
261+ caller. valid_worlds = cycle_valid_worlds
262+ caller. ipo_effects = cycle_effects
257263 finish (caller, caller. interp)
258264 end
259265 for caller in frames
@@ -872,7 +878,8 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize
872878 update_valid_age! (caller, frame. valid_worlds)
873879 isinferred = is_inferred (frame)
874880 edge = isinferred ? mi : nothing
875- effects = isinferred ? frame. result. ipo_effects : adjust_effects (Effects (), method) # effects are adjusted already within `finish` for ipo_effects
881+ effects = isinferred ? frame. result. ipo_effects : # effects are adjusted already within `finish` for ipo_effects
882+ adjust_effects (effects_for_cycle (frame. ipo_effects), method)
876883 exc_bestguess = refine_exception_type (frame. exc_bestguess, effects)
877884 # propagate newly inferred source to the inliner, allowing efficient inlining w/o deserialization:
878885 # note that this result is cached globally exclusively, we can use this local result destructively
@@ -887,11 +894,16 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize
887894 # return the current knowledge about this cycle
888895 frame = frame:: InferenceState
889896 update_valid_age! (caller, frame. valid_worlds)
890- effects = adjust_effects (Effects ( ), method)
897+ effects = adjust_effects (effects_for_cycle (frame . ipo_effects ), method)
891898 exc_bestguess = refine_exception_type (frame. exc_bestguess, effects)
892899 return EdgeCallResult (frame. bestguess, exc_bestguess, nothing , effects)
893900end
894901
902+ # The `:terminates` effect bit must be conservatively tainted unless recursion cycle has
903+ # been fully resolved. As for other effects, there's no need to taint them at this moment
904+ # because they will be tainted as we try to resolve the cycle.
905+ effects_for_cycle (effects:: Effects ) = Effects (effects; terminates= false )
906+
895907function cached_return_type (code:: CodeInstance )
896908 rettype = code. rettype
897909 isdefined (code, :rettype_const ) || return rettype
0 commit comments