PR updates
diff --git a/src/ir/effects.h b/src/ir/effects.h
index a31a994..4d4d5d5 100644
--- a/src/ir/effects.h
+++ b/src/ir/effects.h
@@ -671,27 +671,32 @@
       }
     }
 
+    // Handle effects due to an explicit null check on the given type.
+    // Returns true iff there is no need to consider further effects.
+    bool trapOnNull(Type type) {
+      if (type == Type::unreachable) {
+        return true;
+      }
+      assert(type.isRef());
+      if (type.isNull()) {
+        parent.trap = true;
+        return true;
+      }
+      if (type.isNullable()) {
+        parent.implicitTrap = true;
+      }
+
+      return false;
+    }
+
     // Handle effects due to an explicit null check of the operands in `exprs`.
     // Returns true iff there is no need to consider further effects.
     bool trapOnNull(std::initializer_list<Expression*> exprs) {
       for (auto* expr : exprs) {
-        if (expr && expr->type == Type::unreachable) {
+        if (expr && trapOnNull(expr->type)) {
           return true;
         }
       }
-      for (auto* expr : exprs) {
-        assert(!expr || expr->type.isRef());
-        if (expr && expr->type.isNull()) {
-          parent.trap = true;
-          return true;
-        }
-      }
-      for (auto* expr : exprs) {
-        if (expr && expr->type.isNullable()) {
-          parent.implicitTrap = true;
-          break;
-        }
-      }
       return false;
     }
 
@@ -726,25 +731,25 @@
           target && target->effects) {
         bodyEffects = target->effects.get();
       }
-      populateEffectsForCall(curr, bodyEffects);
+      addCallEffects(curr, bodyEffects);
     }
     void visitCallIndirect(CallIndirect* curr) {
       auto* table = parent.module.getTable(curr->table);
+      if (trapOnNull(table->type)) {
+        return;
+      }
       if (!Type::isSubType(Type(curr->heapType, Nullability::Nullable),
                            table->type)) {
         parent.trap = true;
         return;
       }
-      if (table->type.isNullable()) {
-        parent.implicitTrap = true;
-      }
 
       const EffectAnalyzer* bodyEffects = nullptr;
       if (auto it = parent.module.typeEffects.find(curr->heapType);
-          it != parent.module.typeEffects.end() && it->second) {
+          it != parent.module.typeEffects.end()) {
         bodyEffects = it->second.get();
       }
-      populateEffectsForCall(curr, bodyEffects);
+      addCallEffects(curr, bodyEffects);
     }
     void visitCallRef(CallRef* curr) {
       if (trapOnNull(curr->target)) {
@@ -754,10 +759,10 @@
       const EffectAnalyzer* bodyEffects = nullptr;
       if (auto it =
             parent.module.typeEffects.find(curr->target->type.getHeapType());
-          it != parent.module.typeEffects.end() && it->second) {
+          it != parent.module.typeEffects.end()) {
         bodyEffects = it->second.get();
       }
-      populateEffectsForCall(curr, bodyEffects);
+      addCallEffects(curr, bodyEffects);
     }
     void visitLocalGet(LocalGet* curr) {
       parent.localsRead.insert(curr->index);
@@ -1303,7 +1308,7 @@
     // captured by the function body of the target (e.g. a call_ref may trap on
     // null refs).
     template<typename CallType>
-    void populateFunctionBodyEffects(const CallType* curr,
+    void addCallEffectsFromGlobalEffects(const CallType* curr,
                                      const EffectAnalyzer& funcEffects) {
       if (curr->isReturn) {
         if (funcEffects.throws()) {
@@ -1320,16 +1325,18 @@
       }
     }
 
+    // Common effects logic for the 3 types of call: `call`, `call_indirect`,
+    // and `call_ref`.
     template<typename CallType>
     void
-    populateEffectsForCall(const CallType* curr,
-                           NullablePtr<const EffectAnalyzer*> bodyEffects) {
+    addCallEffects(const CallType* curr,
+                   const EffectAnalyzer* bodyEffects) {
       if (curr->isReturn) {
         parent.branchesOut = true;
       }
 
       if (bodyEffects) {
-        populateFunctionBodyEffects(curr, *bodyEffects);
+        addCallEffectsFromGlobalEffects(curr, *bodyEffects);
         return;
       }
 
diff --git a/src/ir/type-updating.cpp b/src/ir/type-updating.cpp
index 1d4ff36..81bb454 100644
--- a/src/ir/type-updating.cpp
+++ b/src/ir/type-updating.cpp
@@ -326,10 +326,12 @@
   }
 
   // Update indirect call effects per type.
+  // When A is rewritten to B, B inherits the effects of A and A loses its
+  // effects.
   std::unordered_map<HeapType, std::shared_ptr<const EffectAnalyzer>>
     newTypeEffects;
-  for (auto& [oldType, effects] : wasm.typeEffects) {
-    if (!effects) {
+  for (auto& [oldType, oldEffects] : wasm.typeEffects) {
+    if (!oldEffects) {
       continue;
     }
 
@@ -337,10 +339,10 @@
     std::shared_ptr<const EffectAnalyzer>& targetEffects =
       newTypeEffects[newType];
     if (!targetEffects) {
-      targetEffects = effects;
+      targetEffects = oldEffects;
     } else {
       auto merged = std::make_shared<EffectAnalyzer>(*targetEffects);
-      merged->mergeIn(*effects);
+      merged->mergeIn(*oldEffects);
       targetEffects = merged;
     }
   }
diff --git a/src/support/utilities.h b/src/support/utilities.h
index ac1e5cd..272488e 100644
--- a/src/support/utilities.h
+++ b/src/support/utilities.h
@@ -105,8 +105,6 @@
   using Ts::operator()...;
 };
 
-template<typename T> using NullablePtr = T;
-
 } // namespace wasm
 
 #endif // wasm_support_utilities_h
diff --git a/src/wasm.h b/src/wasm.h
index a14416a..7045a3f 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -2722,7 +2722,17 @@
   std::unordered_map<HeapType, TypeNames> typeNames;
   std::unordered_map<HeapType, Index> typeIndices;
 
-  // Potential effects for bodies of indirect calls to this type.
+  // Potential effects for bodies of indirect calls to this type. Populated by
+  // GlobalEffects when --closed-world is enabled. e.g. when we have a call to
+  // HeapType $A and functions $foo and $bar have types that are subtypes of $A,
+  // then an indirect call to $A has effects equal to the union of $foo and $bar.
+  //
+  // When types are rewritten globally, the target type inherits the effects of
+  // source type (see type-updating.cpp). If the type of just one function is
+  // rewritten, we don't update this, because such a rewrite is only valid
+  // if the function is not the target of an indirect call (otherwise the
+  // indirect call would have to be rewritten too).
+  //
   // TODO: Use Type instead of HeapType to account for nullability and
   // exactness.
   std::unordered_map<HeapType, std::shared_ptr<const EffectAnalyzer>>