Range Analysis: Return `IntegerRangeInfo` by value in `GetInfo()`

This patch returns `IntegerRangeInfo` by value instead of pointers
in `IntegerRangeAnalysis::GetInfo()` to prevent potential internal
memory reallocation in the HashMap.

Bug: 348701956
Change-Id: Ic5905a726e0177e5974ecf7748b889156510587a
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/246036
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: James Price <jrprice@google.com>
diff --git a/src/tint/lang/core/ir/analysis/integer_range_analysis.cc b/src/tint/lang/core/ir/analysis/integer_range_analysis.cc
index 1ec3bc2..24bb4d3 100644
--- a/src/tint/lang/core/ir/analysis/integer_range_analysis.cc
+++ b/src/tint/lang/core/ir/analysis/integer_range_analysis.cc
@@ -108,6 +108,10 @@
     range = UnsignedIntegerRange{min_bound, max_bound};
 }
 
+bool IntegerRangeInfo::IsValid() const {
+    return !std::holds_alternative<std::monostate>(range);
+}
+
 struct IntegerRangeAnalysisImpl {
     explicit IntegerRangeAnalysisImpl(Module* ir_module) {
         for (Function* func : ir_module->functions) {
@@ -119,119 +123,109 @@
         }
     }
 
-    const IntegerRangeInfo* GetInfo(const FunctionParam* param, uint32_t index) {
+    IntegerRangeInfo GetInfo(const FunctionParam* param, uint32_t index) {
         const auto& range_info = integer_function_param_range_info_map_.Get(param);
         if (!range_info) {
-            return nullptr;
+            return {};
         }
         TINT_ASSERT(range_info.value->Length() > index);
-        return &(*range_info.value)[index];
+        return (*range_info)[index];
     }
 
-    const IntegerRangeInfo* GetInfo(const Var* var) {
-        return integer_var_range_info_map_.Get(var).value;
-    }
+    IntegerRangeInfo GetInfo(const Var* var) { return integer_var_range_info_map_.GetOr(var, {}); }
 
-    const IntegerRangeInfo* GetInfo(const Load* load) {
+    IntegerRangeInfo GetInfo(const Load* load) {
         const InstructionResult* instruction = load->From()->As<InstructionResult>();
         if (!instruction) {
-            return nullptr;
+            return {};
         }
         const Var* load_from_var = instruction->Instruction()->As<Var>();
         if (!load_from_var) {
-            return nullptr;
+            return {};
         }
         return GetInfo(load_from_var);
     }
 
-    const IntegerRangeInfo* GetInfo(const Access* access) {
+    IntegerRangeInfo GetInfo(const Access* access) {
         const Value* obj = access->Object();
 
         // Currently we only support the access to `local_invocation_id` or `local_invocation_index`
         // as a function parameter.
         const FunctionParam* function_param = obj->As<FunctionParam>();
         if (!function_param) {
-            return nullptr;
+            return {};
         }
         if (access->Indices().Length() > 1) {
-            return nullptr;
+            return {};
         }
         if (!access->Indices()[0]->As<Constant>()) {
-            return nullptr;
+            return {};
         }
         uint32_t index =
             static_cast<uint32_t>(GetValueFromConstant(access->Indices()[0]->As<Constant>()));
         return GetInfo(function_param, index);
     }
 
-    const IntegerRangeInfo* GetInfo(const Let* let) { return GetInfo(let->Value()); }
+    IntegerRangeInfo GetInfo(const Let* let) { return GetInfo(let->Value()); }
 
-    const IntegerRangeInfo* GetInfo(const Constant* constant) {
+    IntegerRangeInfo GetInfo(const Constant* constant) {
         if (!IsConstantInteger(constant)) {
-            return nullptr;
+            return {};
         }
-        const IntegerRangeInfo& range_info =
-            integer_constant_range_info_map_.GetOrAdd(constant, [&]() -> IntegerRangeInfo {
-                int64_t const_value = GetValueFromConstant(constant);
-                return ToIntegerRangeInfo(constant, const_value, const_value);
-            });
-        return &range_info;
+        return integer_constant_range_info_map_.GetOrAdd(constant, [&]() {
+            int64_t const_value = GetValueFromConstant(constant);
+            return ToIntegerRangeInfo(constant, const_value, const_value);
+        });
     }
 
-    const IntegerRangeInfo* GetInfo(const Convert* convert) {
-        const IntegerRangeInfo* existing_range = integer_convert_range_info_map_.Get(convert).value;
-        if (existing_range) {
-            return existing_range;
-        }
-
-        auto* result_type = convert->Result()->Type();
-        if (!result_type->IsIntegerScalar()) {
-            return nullptr;
-        }
-
-        const auto* operand = convert->Operand(Convert::kValueOperandOffset);
-        const IntegerRangeInfo* operand_range_info = GetInfo(operand);
-        if (!operand_range_info) {
-            return nullptr;
-        }
-
-        TINT_ASSERT(operand->Type()->IsIntegerScalar());
-        TINT_ASSERT(operand->Type() != result_type);
-
-        if (std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(
-                operand_range_info->range)) {
-            // result = convert<u32>(operand), operand cannot be negative.
-            TINT_ASSERT(result_type->As<type::U32>());
-            const auto& range =
-                std::get<IntegerRangeInfo::SignedIntegerRange>(operand_range_info->range);
-            if (range.min_bound < 0) {
-                return nullptr;
+    IntegerRangeInfo GetInfo(const Convert* convert) {
+        return integer_convert_range_info_map_.GetOrAdd(convert, [&]() -> IntegerRangeInfo {
+            auto* result_type = convert->Result()->Type();
+            if (!result_type->IsIntegerScalar()) {
+                return {};
             }
-            auto result = integer_convert_range_info_map_.Add(
-                convert, IntegerRangeInfo(static_cast<uint64_t>(range.min_bound),
-                                          static_cast<uint64_t>(range.max_bound)));
-            return &result.value;
-        } else {
-            // result = convert<i32>(operand), operand cannot be greater than `i32::kHighestValue`.
-            TINT_ASSERT(result_type->As<type::I32>());
-            const auto& range =
-                std::get<IntegerRangeInfo::UnsignedIntegerRange>(operand_range_info->range);
-            if (range.max_bound > i32::kHighestValue) {
-                return nullptr;
+
+            const auto* operand = convert->Operand(Convert::kValueOperandOffset);
+            IntegerRangeInfo operand_range_info = GetInfo(operand);
+            if (!operand_range_info.IsValid()) {
+                return {};
             }
-            auto result = integer_convert_range_info_map_.Add(
-                convert, IntegerRangeInfo(static_cast<int64_t>(range.min_bound),
-                                          static_cast<int64_t>(range.max_bound)));
-            return &result.value;
-        }
+
+            TINT_ASSERT(operand->Type()->IsIntegerScalar());
+            TINT_ASSERT(operand->Type() != result_type);
+
+            if (std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(
+                    operand_range_info.range)) {
+                // result = convert<u32>(operand), operand cannot be negative.
+                TINT_ASSERT(result_type->As<type::U32>());
+                const auto& range =
+                    std::get<IntegerRangeInfo::SignedIntegerRange>(operand_range_info.range);
+                if (range.min_bound < 0) {
+                    return {};
+                }
+                return IntegerRangeInfo(static_cast<uint64_t>(range.min_bound),
+                                        static_cast<uint64_t>(range.max_bound));
+            } else {
+                // result = convert<i32>(operand), operand cannot be greater than
+                // `i32::kHighestValue`.
+                TINT_ASSERT(result_type->As<type::I32>());
+                const auto& range =
+                    std::get<IntegerRangeInfo::UnsignedIntegerRange>(operand_range_info.range);
+                if (range.max_bound > i32::kHighestValue) {
+                    return {};
+                }
+                return IntegerRangeInfo(static_cast<int64_t>(range.min_bound),
+                                        static_cast<int64_t>(range.max_bound));
+            }
+        });
     }
 
-    const IntegerRangeInfo* GetInfo(const Value* value) {
+    IntegerRangeInfo GetInfo(const Value* value) {
         return Switch(
             value, [&](const Constant* constant) { return GetInfo(constant); },
-            [&](const FunctionParam* param) -> const IntegerRangeInfo* {
+            [&](const FunctionParam* param) {
                 if (!param->Type()->IsIntegerScalar()) {
-                    return nullptr;
+                    return IntegerRangeInfo();
                 }
                 return GetInfo(param, 0);
             },
@@ -244,49 +238,46 @@
                     [&](const Let* let) { return GetInfo(let); },
                     [&](const Binary* binary) { return GetInfo(binary); },
                     [&](const Convert* convert) { return GetInfo(convert); },
-                    [](Default) { return nullptr; });
+                    [](Default) { return IntegerRangeInfo(); });
             },
-            [](Default) { return nullptr; });
+            [](Default) { return IntegerRangeInfo(); });
     }
 
-    const IntegerRangeInfo* GetInfo(const Binary* binary) {
-        const IntegerRangeInfo* existing_range = integer_binary_range_info_map_.Get(binary).value;
-        if (existing_range) {
-            return existing_range;
-        }
+    IntegerRangeInfo GetInfo(const Binary* binary) {
+        return integer_binary_range_info_map_.GetOrAdd(binary, [&]() -> IntegerRangeInfo {
+            IntegerRangeInfo range_lhs = GetInfo(binary->LHS());
+            if (!range_lhs.IsValid()) {
+                return {};
+            }
+            IntegerRangeInfo range_rhs = GetInfo(binary->RHS());
+            if (!range_rhs.IsValid()) {
+                return {};
+            }
 
-        const IntegerRangeInfo* range_lhs = GetInfo(binary->LHS());
-        if (!range_lhs) {
-            return nullptr;
-        }
-        const IntegerRangeInfo* range_rhs = GetInfo(binary->RHS());
-        if (!range_rhs) {
-            return nullptr;
-        }
+            // TODO(348701956): Support more binary operators
+            switch (binary->Op()) {
+                case BinaryOp::kAdd:
+                    return ComputeIntegerRangeForBinaryAdd(range_lhs, range_rhs);
 
-        // TODO(348701956): Support more binary operators
-        switch (binary->Op()) {
-            case BinaryOp::kAdd:
-                return ComputeAndCacheIntegerRangeForBinaryAdd(binary, range_lhs, range_rhs);
+                case BinaryOp::kSubtract:
+                    return ComputeIntegerRangeForBinarySubtract(range_lhs, range_rhs);
 
-            case BinaryOp::kSubtract:
-                return ComputeAndCacheIntegerRangeForBinarySubtract(binary, range_lhs, range_rhs);
+                case BinaryOp::kMultiply:
+                    return ComputeIntegerRangeForBinaryMultiply(range_lhs, range_rhs);
 
-            case BinaryOp::kMultiply:
-                return ComputeAndCacheIntegerRangeForBinaryMultiply(binary, range_lhs, range_rhs);
+                case BinaryOp::kDivide:
+                    return ComputeIntegerRangeForBinaryDivide(range_lhs, range_rhs);
 
-            case BinaryOp::kDivide:
-                return ComputeAndCacheIntegerRangeForBinaryDivide(binary, range_lhs, range_rhs);
+                case BinaryOp::kShiftLeft:
+                    return ComputeIntegerRangeForBinaryShiftLeft(range_lhs, range_rhs);
 
-            case BinaryOp::kShiftLeft:
-                return ComputeAndCacheIntegerRangeForBinaryShiftLeft(binary, range_lhs, range_rhs);
+                case BinaryOp::kShiftRight:
+                    return ComputeIntegerRangeForBinaryShiftRight(range_lhs, range_rhs);
 
-            case BinaryOp::kShiftRight:
-                return ComputeAndCacheIntegerRangeForBinaryShiftRight(binary, range_lhs, range_rhs);
-
-            default:
-                return nullptr;
-        }
+                default:
+                    return {};
+            }
+        });
     }
 
     /// Analyze a loop to compute the range of the loop control variable if possible.
@@ -808,9 +799,8 @@
         }
     }
 
-    const IntegerRangeInfo* ComputeAndCacheIntegerRangeForBinaryAdd(const Binary* binary,
-                                                                    const IntegerRangeInfo* lhs,
-                                                                    const IntegerRangeInfo* rhs) {
+    IntegerRangeInfo ComputeIntegerRangeForBinaryAdd(const IntegerRangeInfo& lhs,
+                                                     const IntegerRangeInfo& rhs) {
         // Add two 32-bit signed integer values saved in int64_t. Return {} when either overflow or
         // underflow happens.
         auto SafeAddI32 = [](int64_t a, int64_t b) -> std::optional<int64_t> {
@@ -837,39 +827,33 @@
             return sum;
         };
 
-        if (std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(lhs->range)) {
-            auto lhs_i32 = std::get<IntegerRangeInfo::SignedIntegerRange>(lhs->range);
-            auto rhs_i32 = std::get<IntegerRangeInfo::SignedIntegerRange>(rhs->range);
+        if (std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(lhs.range)) {
+            auto lhs_i32 = std::get<IntegerRangeInfo::SignedIntegerRange>(lhs.range);
+            auto rhs_i32 = std::get<IntegerRangeInfo::SignedIntegerRange>(rhs.range);
 
             // [min1, max1] + [min2, max2] => [min1 + min2, max1 + max2]
             std::optional<int64_t> min_bound = SafeAddI32(lhs_i32.min_bound, rhs_i32.min_bound);
             std::optional<int64_t> max_bound = SafeAddI32(lhs_i32.max_bound, rhs_i32.max_bound);
             if (!min_bound.has_value() || !max_bound.has_value()) {
-                return nullptr;
+                return {};
             }
-            auto result = integer_binary_range_info_map_.Add(
-                binary, IntegerRangeInfo(*min_bound, *max_bound));
-            return &result.value;
+            return IntegerRangeInfo(*min_bound, *max_bound);
         } else {
-            auto lhs_u32 = std::get<IntegerRangeInfo::UnsignedIntegerRange>(lhs->range);
-            auto rhs_u32 = std::get<IntegerRangeInfo::UnsignedIntegerRange>(rhs->range);
+            auto lhs_u32 = std::get<IntegerRangeInfo::UnsignedIntegerRange>(lhs.range);
+            auto rhs_u32 = std::get<IntegerRangeInfo::UnsignedIntegerRange>(rhs.range);
 
             // [min1, max1] + [min2, max2] => [min1 + min2, max1 + max2]
             std::optional<uint64_t> min_bound = SafeAddU32(lhs_u32.min_bound, rhs_u32.min_bound);
             std::optional<uint64_t> max_bound = SafeAddU32(lhs_u32.max_bound, rhs_u32.max_bound);
             if (!min_bound || !max_bound) {
-                return nullptr;
+                return {};
             }
-            auto result = integer_binary_range_info_map_.Add(
-                binary, IntegerRangeInfo(*min_bound, *max_bound));
-            return &result.value;
+            return IntegerRangeInfo(*min_bound, *max_bound);
         }
     }
 
-    const IntegerRangeInfo* ComputeAndCacheIntegerRangeForBinarySubtract(
-        const Binary* binary,
-        const IntegerRangeInfo* lhs,
-        const IntegerRangeInfo* rhs) {
+    IntegerRangeInfo ComputeIntegerRangeForBinarySubtract(const IntegerRangeInfo& lhs,
+                                                          const IntegerRangeInfo& rhs) {
         // Subtract two 32-bit signed integer values saved in int64_t. Return {} when either
         // overflow or underflow happens.
         auto SafeSubtractI32 = [](int64_t a, int64_t b) -> std::optional<int64_t> {
@@ -895,9 +879,9 @@
             return a - b;
         };
 
-        if (std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(lhs->range)) {
-            auto lhs_i32 = std::get<IntegerRangeInfo::SignedIntegerRange>(lhs->range);
-            auto rhs_i32 = std::get<IntegerRangeInfo::SignedIntegerRange>(rhs->range);
+        if (std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(lhs.range)) {
+            auto lhs_i32 = std::get<IntegerRangeInfo::SignedIntegerRange>(lhs.range);
+            auto rhs_i32 = std::get<IntegerRangeInfo::SignedIntegerRange>(rhs.range);
 
             // [min1, max1] - [min2, max2] => [min1 - max2, max1 - min2]
             std::optional<int64_t> min_bound =
@@ -905,14 +889,12 @@
             std::optional<int64_t> max_bound =
                 SafeSubtractI32(lhs_i32.max_bound, rhs_i32.min_bound);
             if (!min_bound.has_value() || !max_bound.has_value()) {
-                return nullptr;
+                return {};
             }
-            auto result = integer_binary_range_info_map_.Add(
-                binary, IntegerRangeInfo(*min_bound, *max_bound));
-            return &result.value;
+            return IntegerRangeInfo(*min_bound, *max_bound);
         } else {
-            auto lhs_u32 = std::get<IntegerRangeInfo::UnsignedIntegerRange>(lhs->range);
-            auto rhs_u32 = std::get<IntegerRangeInfo::UnsignedIntegerRange>(rhs->range);
+            auto lhs_u32 = std::get<IntegerRangeInfo::UnsignedIntegerRange>(lhs.range);
+            auto rhs_u32 = std::get<IntegerRangeInfo::UnsignedIntegerRange>(rhs.range);
 
             // [min1, max1] - [min2, max2] => [min1 - max2, max1 - min2]
             std::optional<uint64_t> min_bound =
@@ -920,18 +902,14 @@
             std::optional<uint64_t> max_bound =
                 SafeSubtractU32(lhs_u32.max_bound, rhs_u32.min_bound);
             if (!min_bound || !max_bound) {
-                return nullptr;
+                return {};
             }
-            auto result = integer_binary_range_info_map_.Add(
-                binary, IntegerRangeInfo(*min_bound, *max_bound));
-            return &result.value;
+            return IntegerRangeInfo(*min_bound, *max_bound);
         }
     }
 
-    const IntegerRangeInfo* ComputeAndCacheIntegerRangeForBinaryMultiply(
-        const Binary* binary,
-        const IntegerRangeInfo* lhs,
-        const IntegerRangeInfo* rhs) {
+    IntegerRangeInfo ComputeIntegerRangeForBinaryMultiply(const IntegerRangeInfo& lhs,
+                                                          const IntegerRangeInfo& rhs) {
         // Multiply two 32-bit non-negative signed integer values saved in int64_t. Return {} when
         // overflow happens.
         auto SafeMultiplyNonNegativeI32 = [](int64_t a, int64_t b) -> std::optional<int64_t> {
@@ -958,14 +936,14 @@
             return multiply;
         };
 
-        if (std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(lhs->range)) {
-            auto lhs_i32 = std::get<IntegerRangeInfo::SignedIntegerRange>(lhs->range);
-            auto rhs_i32 = std::get<IntegerRangeInfo::SignedIntegerRange>(rhs->range);
+        if (std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(lhs.range)) {
+            auto lhs_i32 = std::get<IntegerRangeInfo::SignedIntegerRange>(lhs.range);
+            auto rhs_i32 = std::get<IntegerRangeInfo::SignedIntegerRange>(rhs.range);
 
             // Currently we only handle multiplication with non-negative values, which means
             // 0 <= min_bound <= max_bound
             if (lhs_i32.min_bound < 0 || rhs_i32.min_bound < 0) {
-                return nullptr;
+                return {};
             }
 
             // min1 >= 0, min2 >= 0
@@ -975,14 +953,12 @@
             std::optional<int64_t> max_bound =
                 SafeMultiplyNonNegativeI32(lhs_i32.max_bound, rhs_i32.max_bound);
             if (!min_bound.has_value() || !max_bound.has_value()) {
-                return nullptr;
+                return {};
             }
-            auto result = integer_binary_range_info_map_.Add(
-                binary, IntegerRangeInfo(*min_bound, *max_bound));
-            return &result.value;
+            return IntegerRangeInfo(*min_bound, *max_bound);
         } else {
-            auto lhs_u32 = std::get<IntegerRangeInfo::UnsignedIntegerRange>(lhs->range);
-            auto rhs_u32 = std::get<IntegerRangeInfo::UnsignedIntegerRange>(rhs->range);
+            auto lhs_u32 = std::get<IntegerRangeInfo::UnsignedIntegerRange>(lhs.range);
+            auto rhs_u32 = std::get<IntegerRangeInfo::UnsignedIntegerRange>(rhs.range);
 
             // [min1, max1] * [min2, max2] => [min1 * min2, max1 * max2]
             std::optional<uint64_t> min_bound =
@@ -990,76 +966,66 @@
             std::optional<uint64_t> max_bound =
                 SafeMultiplyU32(lhs_u32.max_bound, rhs_u32.max_bound);
             if (!min_bound || !max_bound) {
-                return nullptr;
+                return {};
             }
-            auto result = integer_binary_range_info_map_.Add(
-                binary, IntegerRangeInfo(*min_bound, *max_bound));
-            return &result.value;
+            return IntegerRangeInfo(*min_bound, *max_bound);
         }
     }
 
-    const IntegerRangeInfo* ComputeAndCacheIntegerRangeForBinaryDivide(
-        const Binary* binary,
-        const IntegerRangeInfo* lhs,
-        const IntegerRangeInfo* rhs) {
-        if (std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(lhs->range)) {
-            auto lhs_i32 = std::get<IntegerRangeInfo::SignedIntegerRange>(lhs->range);
-            auto rhs_i32 = std::get<IntegerRangeInfo::SignedIntegerRange>(rhs->range);
+    IntegerRangeInfo ComputeIntegerRangeForBinaryDivide(const IntegerRangeInfo& lhs,
+                                                        const IntegerRangeInfo& rhs) {
+        if (std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(lhs.range)) {
+            auto lhs_i32 = std::get<IntegerRangeInfo::SignedIntegerRange>(lhs.range);
+            auto rhs_i32 = std::get<IntegerRangeInfo::SignedIntegerRange>(rhs.range);
 
             // Currently we require `lhs` must be non-negative, and `rhs` must be positive.
             // 0 <= lhs.min_bound <= lhs.max_bound
             if (lhs_i32.min_bound < 0) {
-                return nullptr;
+                return {};
             }
             // 0 < rhs.min_bound <= rhs.max_bound
             if (rhs_i32.min_bound <= 0) {
-                return nullptr;
+                return {};
             }
 
             // min1 >= 0, min2 > 0
             // [min1, max1] / [min2, max2] => [min1 / max2, max1 / min2]
             int64_t min_bound = lhs_i32.min_bound / rhs_i32.max_bound;
             int64_t max_bound = lhs_i32.max_bound / rhs_i32.min_bound;
-            auto result =
-                integer_binary_range_info_map_.Add(binary, IntegerRangeInfo(min_bound, max_bound));
-            return &result.value;
+            return IntegerRangeInfo(min_bound, max_bound);
         } else {
-            auto lhs_u32 = std::get<IntegerRangeInfo::UnsignedIntegerRange>(lhs->range);
-            auto rhs_u32 = std::get<IntegerRangeInfo::UnsignedIntegerRange>(rhs->range);
+            auto lhs_u32 = std::get<IntegerRangeInfo::UnsignedIntegerRange>(lhs.range);
+            auto rhs_u32 = std::get<IntegerRangeInfo::UnsignedIntegerRange>(rhs.range);
 
             // `rhs` must be positive.
             if (rhs_u32.min_bound == 0) {
-                return nullptr;
+                return {};
             }
 
             // [min1, max1] / [min2, max2] => [min1 / max2, max1 / min2]
             uint64_t min_bound = lhs_u32.min_bound / rhs_u32.max_bound;
             uint64_t max_bound = lhs_u32.max_bound / rhs_u32.min_bound;
-            auto result =
-                integer_binary_range_info_map_.Add(binary, IntegerRangeInfo(min_bound, max_bound));
-            return &result.value;
+            return IntegerRangeInfo(min_bound, max_bound);
         }
     }
 
-    const IntegerRangeInfo* ComputeAndCacheIntegerRangeForBinaryShiftLeft(
-        const Binary* binary,
-        const IntegerRangeInfo* lhs,
-        const IntegerRangeInfo* rhs) {
-        auto rhs_u32 = std::get<IntegerRangeInfo::UnsignedIntegerRange>(rhs->range);
+    IntegerRangeInfo ComputeIntegerRangeForBinaryShiftLeft(const IntegerRangeInfo& lhs,
+                                                           const IntegerRangeInfo& rhs) {
+        auto rhs_u32 = std::get<IntegerRangeInfo::UnsignedIntegerRange>(rhs.range);
 
         // rhs_u32 must be less than the bit width of i32 and u32 (32):
         // rhs_u32.min_bound <= rhs_u32.max_bound < 32
         if (rhs_u32.max_bound >= 32) {
-            return nullptr;
+            return {};
         }
 
-        if (std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(lhs->range)) {
-            auto lhs_i32 = std::get<IntegerRangeInfo::SignedIntegerRange>(lhs->range);
+        if (std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(lhs.range)) {
+            auto lhs_i32 = std::get<IntegerRangeInfo::SignedIntegerRange>(lhs.range);
 
             // Currently we require `lhs` must be non-negative.
             // 0 <= lhs.min_bound <= lhs.max_bound
             if (lhs_i32.min_bound < 0) {
-                return nullptr;
+                return {};
             }
 
             // [min1, max1] << [min2, max2] => [min1 << min2, max1 << max2]
@@ -1069,15 +1035,13 @@
             // Overflow an `i32` is not allowed, which means:
             // min_bound <= max_bound <= i32::kHighestValue
             if (max_bound > static_cast<uint64_t>(i32::kHighestValue)) {
-                return nullptr;
+                return {};
             }
 
             int64_t min_bound = lhs_i32.min_bound << rhs_u32.min_bound;
-            auto result = integer_binary_range_info_map_.Add(
-                binary, IntegerRangeInfo(min_bound, static_cast<int64_t>(max_bound)));
-            return &result.value;
+            return IntegerRangeInfo(min_bound, static_cast<int64_t>(max_bound));
         } else {
-            auto lhs_u32 = std::get<IntegerRangeInfo::UnsignedIntegerRange>(lhs->range);
+            auto lhs_u32 = std::get<IntegerRangeInfo::UnsignedIntegerRange>(lhs.range);
 
             // `max_bound` (as a uint64_t) won't be overflow because `rhs_u32.max_bound` < 32.
             uint64_t max_bound = lhs_u32.max_bound << rhs_u32.max_bound;
@@ -1085,52 +1049,43 @@
             // Overflow a `u32` is not allowed, which means:
             // min_bound <= max_bound <= u32::kHighestValue
             if (max_bound > u32::kHighestValue) {
-                return nullptr;
+                return {};
             }
 
             uint64_t min_bound = lhs_u32.min_bound << rhs_u32.min_bound;
-            auto result =
-                integer_binary_range_info_map_.Add(binary, IntegerRangeInfo(min_bound, max_bound));
-            return &result.value;
+            return IntegerRangeInfo(min_bound, max_bound);
         }
     }
 
-    const IntegerRangeInfo* ComputeAndCacheIntegerRangeForBinaryShiftRight(
-        const Binary* binary,
-        const IntegerRangeInfo* lhs,
-        const IntegerRangeInfo* rhs) {
-        auto rhs_u32 = std::get<IntegerRangeInfo::UnsignedIntegerRange>(rhs->range);
+    IntegerRangeInfo ComputeIntegerRangeForBinaryShiftRight(const IntegerRangeInfo& lhs,
+                                                            const IntegerRangeInfo& rhs) {
+        auto rhs_u32 = std::get<IntegerRangeInfo::UnsignedIntegerRange>(rhs.range);
 
         // rhs_u32 must be less than the bit width of i32 and u32 (32):
         // rhs_u32.min_bound <= rhs_u32.max_bound < 32
         if (rhs_u32.max_bound >= 32) {
-            return nullptr;
+            return {};
         }
 
-        if (std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(lhs->range)) {
-            auto lhs_i32 = std::get<IntegerRangeInfo::SignedIntegerRange>(lhs->range);
+        if (std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(lhs.range)) {
+            auto lhs_i32 = std::get<IntegerRangeInfo::SignedIntegerRange>(lhs.range);
 
             // Currently we require `lhs` must be non-negative.
             // 0 <= lhs.min_bound <= lhs.max_bound
             if (lhs_i32.min_bound < 0) {
-                return nullptr;
+                return {};
             }
 
             // [min1, max1] >> [min2, max2] => [min1 >> max2, max1 >> min2]
             int64_t min_bound = lhs_i32.min_bound >> rhs_u32.max_bound;
             int64_t max_bound = lhs_i32.max_bound >> rhs_u32.min_bound;
-
-            auto result =
-                integer_binary_range_info_map_.Add(binary, IntegerRangeInfo(min_bound, max_bound));
-            return &result.value;
+            return IntegerRangeInfo(min_bound, max_bound);
         } else {
-            auto lhs_u32 = std::get<IntegerRangeInfo::UnsignedIntegerRange>(lhs->range);
+            auto lhs_u32 = std::get<IntegerRangeInfo::UnsignedIntegerRange>(lhs.range);
 
             uint64_t min_bound = lhs_u32.min_bound >> rhs_u32.max_bound;
             uint64_t max_bound = lhs_u32.max_bound >> rhs_u32.min_bound;
-            auto result =
-                integer_binary_range_info_map_.Add(binary, IntegerRangeInfo(min_bound, max_bound));
-            return &result.value;
+            return IntegerRangeInfo(min_bound, max_bound);
         }
     }
 
@@ -1147,7 +1102,7 @@
 
 IntegerRangeAnalysis::~IntegerRangeAnalysis() = default;
 
-const IntegerRangeInfo* IntegerRangeAnalysis::GetInfo(const FunctionParam* param, uint32_t index) {
+IntegerRangeInfo IntegerRangeAnalysis::GetInfo(const FunctionParam* param, uint32_t index) {
     return impl_->GetInfo(param, index);
 }
 
@@ -1169,35 +1124,35 @@
     return impl_->GetCompareInfoOfLoopControlVariableInLoopBody(loop, loop_control_variable).binary;
 }
 
-const IntegerRangeInfo* IntegerRangeAnalysis::GetInfo(const Var* var) {
+IntegerRangeInfo IntegerRangeAnalysis::GetInfo(const Var* var) {
     return impl_->GetInfo(var);
 }
 
-const IntegerRangeInfo* IntegerRangeAnalysis::GetInfo(const Load* load) {
+IntegerRangeInfo IntegerRangeAnalysis::GetInfo(const Load* load) {
     return impl_->GetInfo(load);
 }
 
-const IntegerRangeInfo* IntegerRangeAnalysis::GetInfo(const Access* access) {
+IntegerRangeInfo IntegerRangeAnalysis::GetInfo(const Access* access) {
     return impl_->GetInfo(access);
 }
 
-const IntegerRangeInfo* IntegerRangeAnalysis::GetInfo(const Constant* constant) {
+IntegerRangeInfo IntegerRangeAnalysis::GetInfo(const Constant* constant) {
     return impl_->GetInfo(constant);
 }
 
-const IntegerRangeInfo* IntegerRangeAnalysis::GetInfo(const Value* value) {
+IntegerRangeInfo IntegerRangeAnalysis::GetInfo(const Value* value) {
     return impl_->GetInfo(value);
 }
 
-const IntegerRangeInfo* IntegerRangeAnalysis::GetInfo(const Binary* binary) {
+IntegerRangeInfo IntegerRangeAnalysis::GetInfo(const Binary* binary) {
     return impl_->GetInfo(binary);
 }
 
-const IntegerRangeInfo* IntegerRangeAnalysis::GetInfo(const Let* let) {
+IntegerRangeInfo IntegerRangeAnalysis::GetInfo(const Let* let) {
     return impl_->GetInfo(let);
 }
 
-const IntegerRangeInfo* IntegerRangeAnalysis::GetInfo(const Convert* convert) {
+IntegerRangeInfo IntegerRangeAnalysis::GetInfo(const Convert* convert) {
     return impl_->GetInfo(convert);
 }
 
diff --git a/src/tint/lang/core/ir/analysis/integer_range_analysis.h b/src/tint/lang/core/ir/analysis/integer_range_analysis.h
index b4be69c..e4b79b3 100644
--- a/src/tint/lang/core/ir/analysis/integer_range_analysis.h
+++ b/src/tint/lang/core/ir/analysis/integer_range_analysis.h
@@ -57,6 +57,8 @@
     IntegerRangeInfo(int64_t min_bound, int64_t max_bound);
     IntegerRangeInfo(uint64_t min_bound, uint64_t max_bound);
 
+    bool IsValid() const;
+
     struct SignedIntegerRange {
         int64_t min_bound;
         int64_t max_bound;
@@ -82,47 +84,48 @@
     /// Returns the integer range info of a given parameter with given index, if it is an integer
     /// or an integer vector parameter. The index must not be over the maximum size of the vector
     /// and must be 0 if the parameter is an integer.
-    /// Otherwise is not analyzable and returns nullptr. If it is the first time to query the info,
-    /// the result will also be stored into a cache for future queries.
+    /// Otherwise is not analyzable and returns an invalid `IntegerRangeInfo` object. If it is the
+    /// first time to query the info, the result will also be stored into a cache for future
+    /// queries.
     /// @param param the variable to get information about
     /// @param index the vector component index when the parameter is a vector type. if the
     /// parameter is a scalar, then `index` must be zero.
     /// @returns the integer range info
-    const IntegerRangeInfo* GetInfo(const FunctionParam* param, uint32_t index = 0);
+    IntegerRangeInfo GetInfo(const FunctionParam* param, uint32_t index = 0);
 
     /// Returns the integer range info of a given variable if it is an integer variable and it has a
-    /// meaningful range. Returns nullptr otherwise.
+    /// meaningful range. Returns an invalid `IntegerRangeInfo` object otherwise.
     /// @param var the variable to get information about
     /// @returns the integer range info
-    const IntegerRangeInfo* GetInfo(const Var* var);
+    IntegerRangeInfo GetInfo(const Var* var);
 
     /// Returns the integer range info of a given `Load` variable if it is an integer variable and
-    /// it has a meaningful range. Returns nullptr otherwise.
-    const IntegerRangeInfo* GetInfo(const Load* load_var);
+    /// it has a meaningful range. Returns an invalid `IntegerRangeInfo` object otherwise.
+    IntegerRangeInfo GetInfo(const Load* load_var);
 
     /// Returns the integer range info of a given `Access` variable if it is an integer variable and
-    /// it has a meaningful range. Returns nullptr otherwise.
-    const IntegerRangeInfo* GetInfo(const Access* access);
+    /// it has a meaningful range. Returns an invalid `IntegerRangeInfo` object otherwise.
+    IntegerRangeInfo GetInfo(const Access* access);
 
     /// Returns the integer range info of a given `Let` variable if it is an integer variable and it
-    /// has a meaningful range. Returns nullptr otherwise.
-    const IntegerRangeInfo* GetInfo(const Let* let);
+    /// has a meaningful range. Returns an invalid `IntegerRangeInfo` object otherwise.
+    IntegerRangeInfo GetInfo(const Let* let);
 
     /// Returns the integer range info of a given `Constant` if it is an integer.
-    /// Returns nullptr otherwise.
-    const IntegerRangeInfo* GetInfo(const Constant* constant);
+    /// Returns an invalid `IntegerRangeInfo` object otherwise.
+    IntegerRangeInfo GetInfo(const Constant* constant);
 
     /// Returns the integer range info of a given `Value` variable if it is an integer variable and
-    /// it has a meaningful range. Returns nullptr otherwise.
-    const IntegerRangeInfo* GetInfo(const Value* value);
+    /// it has a meaningful range. Returns an invalid `IntegerRangeInfo` object otherwise.
+    IntegerRangeInfo GetInfo(const Value* value);
 
     /// Returns the integer range info of a given `Binary` variable if it is an integer variable and
-    /// it has a meaningful range. Returns nullptr otherwise.
-    const IntegerRangeInfo* GetInfo(const Binary* binary);
+    /// it has a meaningful range. Returns an invalid `IntegerRangeInfo` object otherwise.
+    IntegerRangeInfo GetInfo(const Binary* binary);
 
     /// Returns the integer range info of a given `Convert` variable if it is an integer variable
-    /// and it has a meaningful range. Returns nullptr otherwise.
-    const IntegerRangeInfo* GetInfo(const Convert* convert);
+    /// and it has a meaningful range. Returns an invalid `IntegerRangeInfo` object otherwise.
+    IntegerRangeInfo GetInfo(const Convert* convert);
 
     /// Note: This function is only for tests.
     /// Returns the pointer of the loop control variable in the given loop when its initializer
diff --git a/src/tint/lang/core/ir/analysis/integer_range_analysis_test.cc b/src/tint/lang/core/ir/analysis/integer_range_analysis_test.cc
index 404ebe6..e9a01b8 100644
--- a/src/tint/lang/core/ir/analysis/integer_range_analysis_test.cc
+++ b/src/tint/lang/core/ir/analysis/integer_range_analysis_test.cc
@@ -67,11 +67,11 @@
     EXPECT_EQ(Validate(mod), Success);
 
     IntegerRangeAnalysis analysis(&mod);
-    auto* info = analysis.GetInfo(localInvocationIndex);
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(localInvocationIndex);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(0u, range.min_bound);
     EXPECT_EQ(23u, range.max_bound);
 }
@@ -103,11 +103,11 @@
     EXPECT_EQ(Validate(mod), Success);
 
     IntegerRangeAnalysis analysis(&mod);
-    auto* info = analysis.GetInfo(localInvocationIndex);
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(localInvocationIndex);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(0u, range.min_bound);
     EXPECT_EQ(59u, range.max_bound);
 }
@@ -139,11 +139,11 @@
     EXPECT_EQ(Validate(mod), Success);
 
     IntegerRangeAnalysis analysis(&mod);
-    auto* info = analysis.GetInfo(localInvocationIndex);
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(localInvocationIndex);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(0u, range.min_bound);
     EXPECT_EQ(7u, range.max_bound);
 }
@@ -175,11 +175,11 @@
     EXPECT_EQ(Validate(mod), Success);
 
     IntegerRangeAnalysis analysis(&mod);
-    auto* info = analysis.GetInfo(localInvocationIndex);
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(localInvocationIndex);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(0u, range.min_bound);
     EXPECT_EQ(15u, range.max_bound);
 }
@@ -232,12 +232,12 @@
 
     std::array<uint32_t, 3> expected_max_bounds = {3u, 2u, 1u};
     for (uint32_t i = 0; i < expected_max_bounds.size(); ++i) {
-        auto* info = analysis.GetInfo(localInvocationId, i);
+        const IntegerRangeInfo& info = analysis.GetInfo(localInvocationId, i);
 
-        ASSERT_NE(nullptr, info);
-        ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
+        ASSERT_TRUE(info.IsValid());
+        ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
 
-        const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+        const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
         EXPECT_EQ(0u, range.min_bound);
         EXPECT_EQ(expected_max_bounds[i], range.max_bound);
     }
@@ -275,12 +275,12 @@
 
     std::array<uint32_t, 3> expected_max_bounds = {0u, 7u, 0u};
     for (uint32_t i = 0; i < expected_max_bounds.size(); ++i) {
-        auto* info = analysis.GetInfo(localInvocationId, i);
+        const IntegerRangeInfo& info = analysis.GetInfo(localInvocationId, i);
 
-        ASSERT_NE(nullptr, info);
-        ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
+        ASSERT_TRUE(info.IsValid());
+        ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
 
-        const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+        const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
         EXPECT_EQ(0u, range.min_bound);
         EXPECT_EQ(expected_max_bounds[i], range.max_bound);
     }
@@ -318,12 +318,12 @@
 
     std::array<uint32_t, 3> expected_max_bounds = {0u, 0u, 15u};
     for (uint32_t i = 0; i < expected_max_bounds.size(); ++i) {
-        auto* info = analysis.GetInfo(localInvocationId, i);
+        const IntegerRangeInfo& info = analysis.GetInfo(localInvocationId, i);
 
-        ASSERT_NE(nullptr, info);
-        ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
+        ASSERT_TRUE(info.IsValid());
+        ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
 
-        const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+        const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
         EXPECT_EQ(0u, range.min_bound);
         EXPECT_EQ(expected_max_bounds[i], range.max_bound);
     }
@@ -1629,11 +1629,11 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(0, range.min_bound);
     EXPECT_EQ(9, range.max_bound);
 }
@@ -1704,11 +1704,11 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(11, range.min_bound);
     EXPECT_EQ(20, range.max_bound);
 }
@@ -1780,11 +1780,11 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(1u, range.min_bound);
     EXPECT_EQ(19u, range.max_bound);
 }
@@ -1855,11 +1855,11 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(11u, range.min_bound);
     EXPECT_EQ(20u, range.max_bound);
 }
@@ -1930,11 +1930,11 @@
     IntegerRangeAnalysis analysis(&mod);
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(1, range.min_bound);
     EXPECT_EQ(10, range.max_bound);
 }
@@ -2005,11 +2005,11 @@
     IntegerRangeAnalysis analysis(&mod);
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(1u, range.min_bound);
     EXPECT_EQ(10u, range.max_bound);
 }
@@ -2080,11 +2080,11 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(10, range.min_bound);
     EXPECT_EQ(20, range.max_bound);
 }
@@ -2155,11 +2155,11 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(10u, range.min_bound);
     EXPECT_EQ(20u, range.max_bound);
 }
@@ -2231,11 +2231,11 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(11, range.min_bound);
     EXPECT_EQ(20, range.max_bound);
 }
@@ -2307,11 +2307,11 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(11u, range.min_bound);
     EXPECT_EQ(20u, range.max_bound);
 }
@@ -2383,11 +2383,11 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(0, range.min_bound);
     EXPECT_EQ(9, range.max_bound);
 }
@@ -2459,11 +2459,11 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(0u, range.min_bound);
     EXPECT_EQ(9u, range.max_bound);
 }
@@ -2535,11 +2535,11 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(10, range.min_bound);
     EXPECT_EQ(20, range.max_bound);
 }
@@ -2611,11 +2611,11 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(10u, range.min_bound);
     EXPECT_EQ(20u, range.max_bound);
 }
@@ -2687,11 +2687,11 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(0, range.min_bound);
     EXPECT_EQ(10, range.max_bound);
 }
@@ -2763,11 +2763,11 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(0u, range.min_bound);
     EXPECT_EQ(10u, range.max_bound);
 }
@@ -5063,12 +5063,12 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
 
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
 
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(10, range.min_bound);
     EXPECT_EQ(10, range.max_bound);
 }
@@ -5140,12 +5140,12 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
 
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
 
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(10u, range.min_bound);
     EXPECT_EQ(10u, range.max_bound);
 }
@@ -5217,8 +5217,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
 
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AnalyzeLoop_Failure_LessThanEqual_init_greater_than_rhs_u32) {
@@ -5288,8 +5288,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
 
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AnalyzeLoop_Failure_index_LessThanEqual_constant_decreasing) {
@@ -5359,8 +5359,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
 
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AnalyzeLoop_Success_GreaterThanEqual_init_equals_rhs_i32) {
@@ -5430,11 +5430,11 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(20, range.min_bound);
     EXPECT_EQ(20, range.max_bound);
 }
@@ -5506,11 +5506,11 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(20u, range.min_bound);
     EXPECT_EQ(20u, range.max_bound);
 }
@@ -5582,8 +5582,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AnalyzeLoop_Failure_GreaterThanEqual_init_less_than_rhs_u32) {
@@ -5653,8 +5653,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest,
@@ -5725,8 +5725,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AnalyzeLoop_Success_index_LessThanEqual_max_u32_minus_1) {
@@ -5795,11 +5795,11 @@
     IntegerRangeAnalysis analysis(&mod);
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(u32::kHighestValue - 1u, range.min_bound);
     EXPECT_EQ(u32::kHighestValue - 1u, range.max_bound);
 }
@@ -5870,11 +5870,11 @@
     IntegerRangeAnalysis analysis(&mod);
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(i32::kHighestValue - 1, range.min_bound);
     EXPECT_EQ(i32::kHighestValue - 1, range.max_bound);
 }
@@ -5945,11 +5945,11 @@
     IntegerRangeAnalysis analysis(&mod);
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(u32::kLowestValue + 1u, range.min_bound);
     EXPECT_EQ(u32::kLowestValue + 1u, range.max_bound);
 }
@@ -6020,11 +6020,11 @@
     IntegerRangeAnalysis analysis(&mod);
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(i32::kLowestValue + 1, range.min_bound);
     EXPECT_EQ(i32::kLowestValue + 1, range.max_bound);
 }
@@ -6096,8 +6096,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
 
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AnalyzeLoop_Failure_LessThan_init_equals_rhs_i32) {
@@ -6167,8 +6167,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
 
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AnalyzeLoop_Failure_LessThan_init_greater_than_rhs_u32) {
@@ -6238,8 +6238,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
 
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AnalyzeLoop_Failure_LessThan_init_equals_rhs_u32) {
@@ -6309,8 +6309,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
 
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AnalyzeLoop_Failure_index_LessThan_constant_decreasing) {
@@ -6380,8 +6380,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
 
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AnalyzeLoop_Failure_GreaterThan_init_less_than_rhs_i32) {
@@ -6451,8 +6451,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
 
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AnalyzeLoop_Failure_GreaterThan_init_equals_rhs_i32) {
@@ -6522,8 +6522,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
 
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AnalyzeLoop_Failure_GreaterThan_init_less_than_rhs_u32) {
@@ -6593,8 +6593,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
 
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AnalyzeLoop_Failure_GreaterThan_init_equals_rhs_u32) {
@@ -6664,8 +6664,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
 
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AnalyzeLoop_Failure_index_GreaterThan_constant_increasing) {
@@ -6735,8 +6735,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
 
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 // const_lhs <= index
@@ -6806,11 +6806,11 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(20, range.min_bound);
     EXPECT_EQ(20, range.max_bound);
 }
@@ -6881,11 +6881,11 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(20u, range.min_bound);
     EXPECT_EQ(20u, range.max_bound);
 }
@@ -6957,8 +6957,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AnalyzeLoopBody_Failure_LessThanEqual_init_LessThan_lhs_u32) {
@@ -7028,8 +7028,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest,
@@ -7100,8 +7100,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AnalyzeLoop_Success_min_u32_add_1_LessThanEqual_index) {
@@ -7170,11 +7170,11 @@
     IntegerRangeAnalysis analysis(&mod);
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(u32::kLowestValue + 1u, range.min_bound);
     EXPECT_EQ(u32::kLowestValue + 1u, range.max_bound);
 }
@@ -7245,11 +7245,11 @@
     IntegerRangeAnalysis analysis(&mod);
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(i32::kLowestValue + 1, range.min_bound);
     EXPECT_EQ(i32::kLowestValue + 1, range.max_bound);
 }
@@ -7322,11 +7322,11 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(20, range.min_bound);
     EXPECT_EQ(20, range.max_bound);
 }
@@ -7398,11 +7398,11 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(20u, range.min_bound);
     EXPECT_EQ(20u, range.max_bound);
 }
@@ -7475,8 +7475,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest,
@@ -7547,8 +7547,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest,
@@ -7619,8 +7619,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AnalyzeLoop_Success_max_u32_minus_1_GreaterThanEqual_index) {
@@ -7689,11 +7689,11 @@
     IntegerRangeAnalysis analysis(&mod);
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(u32::kHighestValue - 1u, range.min_bound);
     EXPECT_EQ(u32::kHighestValue - 1u, range.max_bound);
 }
@@ -7764,11 +7764,11 @@
     IntegerRangeAnalysis analysis(&mod);
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(i32::kHighestValue - 1, range.min_bound);
     EXPECT_EQ(i32::kHighestValue - 1, range.max_bound);
 }
@@ -7841,8 +7841,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AnalyzeLoopBody_Failure_LessThan_init_equals_lhs_u32) {
@@ -7912,8 +7912,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AnalyzeLoopBody_Failure_LessThan_init_LessThan_lhs_i32) {
@@ -7983,8 +7983,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AnalyzeLoopBody_Failure_LessThan_init_LessThan_lhs_u32) {
@@ -8054,8 +8054,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AnalyzeLoopBody_Failure_constant_LessThan_index_increasing) {
@@ -8125,8 +8125,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 // lhs > index
@@ -8197,8 +8197,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AnalyzeLoopBody_Success_GreaterThan_init_equals_lhs_u32) {
@@ -8268,8 +8268,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AnalyzeLoopBody_Failure_GreaterThan_init_GreaterThan_lhs_i32) {
@@ -8339,8 +8339,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AnalyzeLoopBody_Failure_GreaterThan_init_GreaterThan_lhs_u32) {
@@ -8410,8 +8410,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AnalyzeLoopBody_Failure_constant_GreaterThan_index_decreasing) {
@@ -8481,8 +8481,8 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, info);
+    const IntegerRangeInfo& info = analysis.GetInfo(idx);
+    EXPECT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, LoadFromLoopControlVariableWithRange) {
@@ -8554,20 +8554,20 @@
 
     IntegerRangeAnalysis analysis(&mod);
 
-    const IntegerRangeInfo* idx_info = analysis.GetInfo(idx);
-    EXPECT_NE(nullptr, idx_info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(idx_info->range));
+    const IntegerRangeInfo& idx_info = analysis.GetInfo(idx);
+    EXPECT_TRUE(idx_info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(idx_info.range));
 
-    const auto& range_idx = std::get<IntegerRangeInfo::SignedIntegerRange>(idx_info->range);
+    const auto& range_idx = std::get<IntegerRangeInfo::SignedIntegerRange>(idx_info.range);
     EXPECT_EQ(0, range_idx.min_bound);
     EXPECT_EQ(9, range_idx.max_bound);
 
-    const IntegerRangeInfo* load_idx_info = analysis.GetInfo(load_idx);
-    EXPECT_NE(nullptr, load_idx_info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(load_idx_info->range));
+    const IntegerRangeInfo& load_idx_info = analysis.GetInfo(load_idx);
+    EXPECT_TRUE(load_idx_info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(load_idx_info.range));
 
     const auto& range_load_idx =
-        std::get<IntegerRangeInfo::SignedIntegerRange>(load_idx_info->range);
+        std::get<IntegerRangeInfo::SignedIntegerRange>(load_idx_info.range);
     EXPECT_EQ(0, range_load_idx.min_bound);
     EXPECT_EQ(9, range_load_idx.max_bound);
 }
@@ -8644,10 +8644,10 @@
     EXPECT_EQ(idx, analysis.GetLoopControlVariableFromConstantInitializerForTest(loop));
     EXPECT_EQ(binary, analysis.GetBinaryToCompareLoopControlVariableInLoopBodyForTest(loop, idx));
 
-    const IntegerRangeInfo* idx_info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, idx_info);
-    const IntegerRangeInfo* load_idx_info = analysis.GetInfo(idx);
-    EXPECT_EQ(nullptr, load_idx_info);
+    const IntegerRangeInfo& idx_info = analysis.GetInfo(idx);
+    EXPECT_FALSE(idx_info.IsValid());
+    const IntegerRangeInfo& load_idx_info = analysis.GetInfo(idx);
+    EXPECT_FALSE(load_idx_info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, LoadFromNonLoopControlVariable) {
@@ -8677,7 +8677,7 @@
     EXPECT_EQ(Validate(mod), Success);
 
     IntegerRangeAnalysis analysis(&mod);
-    EXPECT_EQ(nullptr, analysis.GetInfo(load_a));
+    EXPECT_FALSE(analysis.GetInfo(load_a).IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AccessToLocalInvocationID) {
@@ -8711,30 +8711,30 @@
 
     IntegerRangeAnalysis analysis(&mod);
 
-    auto* access_x_info = analysis.GetInfo(access_x);
-    ASSERT_NE(nullptr, access_x_info);
+    const auto& access_x_info = analysis.GetInfo(access_x);
+    ASSERT_TRUE(access_x_info.IsValid());
     ASSERT_TRUE(
-        std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(access_x_info->range));
+        std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(access_x_info.range));
     const auto& range_access_x =
-        std::get<IntegerRangeInfo::UnsignedIntegerRange>(access_x_info->range);
+        std::get<IntegerRangeInfo::UnsignedIntegerRange>(access_x_info.range);
     EXPECT_EQ(0u, range_access_x.min_bound);
     EXPECT_EQ(3u, range_access_x.max_bound);
 
-    auto* access_y_info = analysis.GetInfo(access_y);
-    ASSERT_NE(nullptr, access_x_info);
+    const auto& access_y_info = analysis.GetInfo(access_y);
+    ASSERT_TRUE(access_x_info.IsValid());
     ASSERT_TRUE(
-        std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(access_y_info->range));
+        std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(access_y_info.range));
     const auto& range_access_y =
-        std::get<IntegerRangeInfo::UnsignedIntegerRange>(access_y_info->range);
+        std::get<IntegerRangeInfo::UnsignedIntegerRange>(access_y_info.range);
     EXPECT_EQ(0u, range_access_y.min_bound);
     EXPECT_EQ(2u, range_access_y.max_bound);
 
-    auto* access_z_info = analysis.GetInfo(access_z);
-    ASSERT_NE(nullptr, access_x_info);
+    const auto& access_z_info = analysis.GetInfo(access_z);
+    ASSERT_TRUE(access_x_info.IsValid());
     ASSERT_TRUE(
-        std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(access_z_info->range));
+        std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(access_z_info.range));
     const auto& range_access_z =
-        std::get<IntegerRangeInfo::UnsignedIntegerRange>(access_z_info->range);
+        std::get<IntegerRangeInfo::UnsignedIntegerRange>(access_z_info.range);
     EXPECT_EQ(0u, range_access_z.min_bound);
     EXPECT_EQ(1u, range_access_z.max_bound);
 }
@@ -8761,8 +8761,8 @@
     EXPECT_EQ(Validate(mod), Success);
 
     IntegerRangeAnalysis analysis(&mod);
-    auto* info = analysis.GetInfo(access);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(access);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AccessToFunctionParamNoRange) {
@@ -8795,9 +8795,9 @@
     EXPECT_EQ(Validate(mod), Success);
 
     IntegerRangeAnalysis analysis(&mod);
-    ASSERT_EQ(nullptr, analysis.GetInfo(access_x));
-    ASSERT_EQ(nullptr, analysis.GetInfo(access_y));
-    ASSERT_EQ(nullptr, analysis.GetInfo(access_z));
+    ASSERT_FALSE(analysis.GetInfo(access_x).IsValid());
+    ASSERT_FALSE(analysis.GetInfo(access_y).IsValid());
+    ASSERT_FALSE(analysis.GetInfo(access_z).IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, AccessToNonIntegerFunctionParam) {
@@ -8822,8 +8822,8 @@
     EXPECT_EQ(Validate(mod), Success);
 
     IntegerRangeAnalysis analysis(&mod);
-    auto* info = analysis.GetInfo(access);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(access);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, NonConstantAccessIndex) {
@@ -8855,8 +8855,8 @@
 
     IntegerRangeAnalysis analysis(&mod);
 
-    auto* access_x_info = analysis.GetInfo(access_x);
-    ASSERT_EQ(nullptr, access_x_info);
+    const auto& access_x_info = analysis.GetInfo(access_x);
+    ASSERT_FALSE(access_x_info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, SignedIntegerScalarConstant) {
@@ -8880,10 +8880,10 @@
     EXPECT_EQ(Validate(mod), Success);
 
     IntegerRangeAnalysis analysis(&mod);
-    auto* info = analysis.GetInfo(constant);
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& info = analysis.GetInfo(constant);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(10, range.min_bound);
     EXPECT_EQ(10, range.max_bound);
 }
@@ -8909,10 +8909,10 @@
     EXPECT_EQ(Validate(mod), Success);
 
     IntegerRangeAnalysis analysis(&mod);
-    auto* info = analysis.GetInfo(constant);
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& info = analysis.GetInfo(constant);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(20u, range.min_bound);
     EXPECT_EQ(20u, range.max_bound);
 }
@@ -8938,8 +8938,8 @@
     EXPECT_EQ(Validate(mod), Success);
 
     IntegerRangeAnalysis analysis(&mod);
-    auto* info = analysis.GetInfo(constant);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(constant);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, ValueAsScalarFunctionParameter) {
@@ -8969,18 +8969,18 @@
     IntegerRangeAnalysis analysis(&mod);
 
     // Range of `add->LHS()` (`localInvocationIndex`)
-    auto* info = analysis.GetInfo(add->LHS());
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& info = analysis.GetInfo(add->LHS());
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(0u, range.min_bound);
     EXPECT_EQ(23u, range.max_bound);
 
     // Range of `add` (`localInvocationIndex + 5`)
-    auto* info_add = analysis.GetInfo(add);
-    ASSERT_NE(nullptr, info_add);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info_add->range));
-    const auto& range_add = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info_add->range);
+    const auto& info_add = analysis.GetInfo(add);
+    ASSERT_TRUE(info_add.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info_add.range));
+    const auto& range_add = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info_add.range);
     EXPECT_EQ(5u, range_add.min_bound);
     EXPECT_EQ(28u, range_add.max_bound);
 }
@@ -9011,8 +9011,8 @@
 
     IntegerRangeAnalysis analysis(&mod);
 
-    auto* info = analysis.GetInfo(value);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(value);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, ValueAsAccess) {
@@ -9046,26 +9046,26 @@
     IntegerRangeAnalysis analysis(&mod);
 
     // Range of `add->LHS()` (`local_id.x`)
-    auto* info_lhs = analysis.GetInfo(add->LHS());
-    ASSERT_NE(nullptr, info_lhs);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info_lhs->range));
-    const auto& range_lhs = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info_lhs->range);
+    const auto& info_lhs = analysis.GetInfo(add->LHS());
+    ASSERT_TRUE(info_lhs.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info_lhs.range));
+    const auto& range_lhs = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info_lhs.range);
     EXPECT_EQ(0u, range_lhs.min_bound);
     EXPECT_EQ(3u, range_lhs.max_bound);
 
     // Range of `add->RHS()` (`local_id.y`)
-    auto* info_rhs = analysis.GetInfo(add->RHS());
-    ASSERT_NE(nullptr, info_rhs);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info_rhs->range));
-    const auto& range_rhs = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info_rhs->range);
+    const auto& info_rhs = analysis.GetInfo(add->RHS());
+    ASSERT_TRUE(info_rhs.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info_rhs.range));
+    const auto& range_rhs = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info_rhs.range);
     EXPECT_EQ(0u, range_rhs.min_bound);
     EXPECT_EQ(2u, range_rhs.max_bound);
 
     // Range of `add` (`local_id.x + local_id.y`)
-    auto* info_add = analysis.GetInfo(add);
-    ASSERT_NE(nullptr, info_add);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info_add->range));
-    const auto& range_add = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info_add->range);
+    const auto& info_add = analysis.GetInfo(add);
+    ASSERT_TRUE(info_add.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info_add.range));
+    const auto& range_add = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info_add.range);
     EXPECT_EQ(0u, range_add.min_bound);
     EXPECT_EQ(5u, range_add.max_bound);
 }
@@ -9139,26 +9139,26 @@
     IntegerRangeAnalysis analysis(&mod);
 
     // Range of `add->LHS()` (`idx`)
-    auto* info_lhs = analysis.GetInfo(add->LHS());
-    ASSERT_NE(nullptr, info_lhs);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info_lhs->range));
-    const auto& range_lhs = std::get<IntegerRangeInfo::SignedIntegerRange>(info_lhs->range);
+    const auto& info_lhs = analysis.GetInfo(add->LHS());
+    ASSERT_TRUE(info_lhs.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info_lhs.range));
+    const auto& range_lhs = std::get<IntegerRangeInfo::SignedIntegerRange>(info_lhs.range);
     EXPECT_EQ(0, range_lhs.min_bound);
     EXPECT_EQ(9, range_lhs.max_bound);
 
     // Range of `add->RHS()` (5)
-    auto* info_rhs = analysis.GetInfo(add->RHS());
-    ASSERT_NE(nullptr, info_rhs);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info_rhs->range));
-    const auto& range_rhs = std::get<IntegerRangeInfo::SignedIntegerRange>(info_rhs->range);
+    const auto& info_rhs = analysis.GetInfo(add->RHS());
+    ASSERT_TRUE(info_rhs.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info_rhs.range));
+    const auto& range_rhs = std::get<IntegerRangeInfo::SignedIntegerRange>(info_rhs.range);
     EXPECT_EQ(5, range_rhs.min_bound);
     EXPECT_EQ(5, range_rhs.max_bound);
 
     // Range of `add` (`idx + 5`)
-    auto* info_add = analysis.GetInfo(add);
-    ASSERT_NE(nullptr, info_add);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info_add->range));
-    const auto& range_add = std::get<IntegerRangeInfo::SignedIntegerRange>(info_add->range);
+    const auto& info_add = analysis.GetInfo(add);
+    ASSERT_TRUE(info_add.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info_add.range));
+    const auto& range_add = std::get<IntegerRangeInfo::SignedIntegerRange>(info_add.range);
     EXPECT_EQ(5, range_add.min_bound);
     EXPECT_EQ(14, range_add.max_bound);
 }
@@ -9235,18 +9235,18 @@
     IntegerRangeAnalysis analysis(&mod);
 
     // Range of `value`
-    auto* info = analysis.GetInfo(value);
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& info = analysis.GetInfo(value);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(0, range.min_bound);
     EXPECT_EQ(9, range.max_bound);
 
     // Range of `add` (`value + 5`)
-    auto* info_add = analysis.GetInfo(add);
-    ASSERT_NE(nullptr, info_add);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info_add->range));
-    const auto& range_add = std::get<IntegerRangeInfo::SignedIntegerRange>(info_add->range);
+    const auto& info_add = analysis.GetInfo(add);
+    ASSERT_TRUE(info_add.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info_add.range));
+    const auto& range_add = std::get<IntegerRangeInfo::SignedIntegerRange>(info_add.range);
     EXPECT_EQ(5, range_add.min_bound);
     EXPECT_EQ(14, range_add.max_bound);
 }
@@ -9279,9 +9279,9 @@
 
     IntegerRangeAnalysis analysis(&mod);
 
-    auto* info = analysis.GetInfo(let);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& info = analysis.GetInfo(let);
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(0u, range.min_bound);
     EXPECT_EQ(3u, range.max_bound);
 }
@@ -9354,9 +9354,9 @@
 
     IntegerRangeAnalysis analysis(&mod);
 
-    auto* info = analysis.GetInfo(let);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& info = analysis.GetInfo(let);
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(0, range.min_bound);
     EXPECT_EQ(9, range.max_bound);
 }
@@ -9394,9 +9394,9 @@
 
     IntegerRangeAnalysis analysis(&mod);
 
-    auto* info = analysis.GetInfo(let);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& info = analysis.GetInfo(let);
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(0u, range.min_bound);
     EXPECT_EQ(5u, range.max_bound);
 }
@@ -9434,15 +9434,15 @@
 
     IntegerRangeAnalysis analysis(&mod);
 
-    auto* info_let = analysis.GetInfo(let);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info_let->range));
-    const auto& range_let = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info_let->range);
+    const auto& info_let = analysis.GetInfo(let);
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info_let.range));
+    const auto& range_let = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info_let.range);
     EXPECT_EQ(0u, range_let.min_bound);
     EXPECT_EQ(3u, range_let.max_bound);
 
-    auto* info_add = analysis.GetInfo(add);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info_add->range));
-    const auto& range_add = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info_add->range);
+    const auto& info_add = analysis.GetInfo(add);
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info_add.range));
+    const auto& range_add = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info_add.range);
     EXPECT_EQ(0u, range_add.min_bound);
     EXPECT_EQ(5u, range_add.max_bound);
 }
@@ -9531,10 +9531,10 @@
 
     // Range of `add` (`idx + (local_id.x + local_id.y)`)
     // access_x: [0, 3], access_y: [0, 2], idx: [0, 9]
-    auto* info_add = analysis.GetInfo(add);
-    ASSERT_NE(nullptr, info_add);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info_add->range));
-    const auto& range_add = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info_add->range);
+    const auto& info_add = analysis.GetInfo(add);
+    ASSERT_TRUE(info_add.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info_add.range));
+    const auto& range_add = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info_add.range);
     EXPECT_EQ(0u, range_add.min_bound);
     EXPECT_EQ(14u, range_add.max_bound);
 }
@@ -9571,8 +9571,8 @@
 
     // Range of `add` (`local_id.x + 4294967289`)
     // local_id.x: [0, 7], 4294967289 > u32::kHighestValue - 7
-    auto* info = analysis.GetInfo(add);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(add);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, BinaryAdd_I32_Overflow) {
@@ -9648,8 +9648,8 @@
 
     // Range of `add` (`idx + 2147483639`)
     // idx: [0, 9], 2147483639 > i32::kHighestValue - 9
-    auto* info = analysis.GetInfo(add);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(add);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, BinaryAdd_I32_Underflow) {
@@ -9725,8 +9725,8 @@
 
     // Range of `add` (`idx + (-2147483640)`)
     // idx: [-9, 0], -2147483640 < i32::kLowestValue + 9
-    auto* info = analysis.GetInfo(add);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(add);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, BinarySubtract_Success_U32) {
@@ -9808,10 +9808,10 @@
 
     // Range of `subtract` (`idx - local_id.x`)
     // idx: [4, 9] local_id.x: [0, 3]
-    auto* info = analysis.GetInfo(subtract);
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& info = analysis.GetInfo(subtract);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(1u, range.min_bound);
     EXPECT_EQ(9u, range.max_bound);
 }
@@ -9935,10 +9935,10 @@
 
     // Range of `subtract` (`idx - idy`)
     // idx: [5, 9], idy: [1, 3]
-    auto* info = analysis.GetInfo(subtract);
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& info = analysis.GetInfo(subtract);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(2, range.min_bound);
     EXPECT_EQ(8, range.max_bound);
 }
@@ -10062,8 +10062,8 @@
 
     // Range of `subtract` (`idx - idy`)
     // idx: [5, 9], idy: [1, 7], idx.min_bound < idy.max_bound
-    auto* info = analysis.GetInfo(subtract);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(subtract);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, BinarySubtract_Failure_Overflow_I32) {
@@ -10139,8 +10139,8 @@
 
     // Range of `subtract` (`idx - (-2147483640)`)
     // idx: [0, 8], 2147483640 > i32::kHighestValue - 8
-    auto* info = analysis.GetInfo(subtract);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(subtract);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, BinarySubtract_Failure_Underflow_I32) {
@@ -10216,8 +10216,8 @@
 
     // Range of `subtract` (`idx - 2147483640`)
     // idx: [-9, 0], idx < i32::kLowestValue + 2147483640
-    auto* info = analysis.GetInfo(subtract);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(subtract);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, BinaryMultiply_Success_I32) {
@@ -10339,10 +10339,10 @@
 
     // Range of `multiply` (`idx * idy`)
     // idx: [5, 9], idy: [1, 3]
-    auto* info = analysis.GetInfo(multiply);
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& info = analysis.GetInfo(multiply);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(5, range.min_bound);
     EXPECT_EQ(27, range.max_bound);
 }
@@ -10466,10 +10466,10 @@
 
     // Range of `multiply` (`idx * idy`)
     // idx: [3, 10], idy: [6, 19]
-    auto* info = analysis.GetInfo(multiply);
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& info = analysis.GetInfo(multiply);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(18u, range.min_bound);
     EXPECT_EQ(190u, range.max_bound);
 }
@@ -10593,8 +10593,8 @@
 
     // Range of `multiply` (`idx * idy`)
     // idx: [-5, 9], idy: [1, 3]
-    auto* info = analysis.GetInfo(multiply);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(multiply);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, BinaryMultiply_Failure_negative_rhs) {
@@ -10716,8 +10716,8 @@
 
     // Range of `multiply` (`idx * idy`)
     // idx: [5, 9], idy: [-1, 3]
-    auto* info = analysis.GetInfo(multiply);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(multiply);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, BinaryMultiply_Failure_Overflow_MaxBound_I32) {
@@ -10793,8 +10793,8 @@
 
     // Range of `multiply` (`idx * 268435456`)
     // idx: [0, 8], 268435456 > i32::kHighestValue / 8
-    auto* info = analysis.GetInfo(multiply);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(multiply);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, BinaryMultiply_Failure_Overflow_MinBound_I32) {
@@ -10870,8 +10870,8 @@
 
     // Range of `multiply` (`idx * 134217728`)
     // idx: [16, 20], 134217728 > i32::kHighestValue / 16
-    auto* info = analysis.GetInfo(multiply);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(multiply);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, BinaryMultiply_Failure_Overflow_MaxBound_U32) {
@@ -10947,8 +10947,8 @@
 
     // Range of `multiply` (`idx * 536870912`)
     // idx: [0, 8], 536870912 > u32::kHighestValue / 8
-    auto* info = analysis.GetInfo(multiply);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(multiply);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, BinaryMultiply_Failure_Overflow_MinBound_U32) {
@@ -11024,8 +11024,8 @@
 
     // Range of `multiply` (`idx * 268435456`)
     // idx: [16, 20], 268435456 > u32::kHighestValue / 16
-    auto* info = analysis.GetInfo(multiply);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(multiply);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, Convert_Success_U32ToI32) {
@@ -11052,11 +11052,11 @@
     EXPECT_EQ(Validate(mod), Success);
 
     IntegerRangeAnalysis analysis(&mod);
-    auto* info = analysis.GetInfo(convert);
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
+    const auto& info = analysis.GetInfo(convert);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(0, range.min_bound);
     EXPECT_EQ(23, range.max_bound);
 }
@@ -11128,11 +11128,11 @@
 
     IntegerRangeAnalysis analysis(&mod);
 
-    auto* info = analysis.GetInfo(convert);
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
+    const auto& info = analysis.GetInfo(convert);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
 
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(1u, range.min_bound);
     EXPECT_EQ(9u, range.max_bound);
 }
@@ -11204,8 +11204,8 @@
 
     IntegerRangeAnalysis analysis(&mod);
 
-    auto* info = analysis.GetInfo(convert);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(convert);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, Convert_Failure_LargeU32ToI32) {
@@ -11276,8 +11276,8 @@
 
     IntegerRangeAnalysis analysis(&mod);
 
-    auto* info = analysis.GetInfo(convert);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(convert);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, Convert_Failure_ConvertToNonInteger) {
@@ -11347,8 +11347,8 @@
 
     IntegerRangeAnalysis analysis(&mod);
 
-    auto* info = analysis.GetInfo(convert);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(convert);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, Divide_Success_Divisible_I32) {
@@ -11471,10 +11471,10 @@
 
     // Range of `divide` (`idx / idy`)
     // idx: [4, 8], idy: [1, 2]
-    auto* info = analysis.GetInfo(divide);
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& info = analysis.GetInfo(divide);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(2, range.min_bound);
     EXPECT_EQ(8, range.max_bound);
 }
@@ -11599,10 +11599,10 @@
 
     // Range of `divide` (`idx / idy`)
     // idx: [4u, 8u], idy: [1u, 2u]
-    auto* info = analysis.GetInfo(divide);
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& info = analysis.GetInfo(divide);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(2u, range.min_bound);
     EXPECT_EQ(8u, range.max_bound);
 }
@@ -11727,10 +11727,10 @@
 
     // Range of `divide` (`idx / idy`)
     // idx: [8, 16], idy: [2, 3]
-    auto* info = analysis.GetInfo(divide);
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& info = analysis.GetInfo(divide);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(2, range.min_bound);
     EXPECT_EQ(8, range.max_bound);
 }
@@ -11855,10 +11855,10 @@
 
     // Range of `divide` (`idx / idy`)
     // idx: [8, 16], idy: [6, 9]
-    auto* info = analysis.GetInfo(divide);
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& info = analysis.GetInfo(divide);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(0, range.min_bound);
     EXPECT_EQ(2, range.max_bound);
 }
@@ -11983,10 +11983,10 @@
 
     // Range of `divide` (`idx / idy`)
     // idx: [0, 16], idy: [4, 8]
-    auto* info = analysis.GetInfo(divide);
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& info = analysis.GetInfo(divide);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(0, range.min_bound);
     EXPECT_EQ(4, range.max_bound);
 }
@@ -12111,10 +12111,10 @@
 
     // Range of `divide` (`idx / idy`)
     // idx: [0, 16u], idy: [4u, 8u]
-    auto* info = analysis.GetInfo(divide);
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& info = analysis.GetInfo(divide);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(0u, range.min_bound);
     EXPECT_EQ(4u, range.max_bound);
 }
@@ -12239,8 +12239,8 @@
 
     // Range of `divide` (`idx / idy`)
     // idx: [-1, 16], idy: [4, 8]
-    auto* info = analysis.GetInfo(divide);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(divide);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, Divide_Failure_NegativeRHS) {
@@ -12363,8 +12363,8 @@
 
     // Range of `divide` (`idx / idy`)
     // idx: [0, 16], idy: [-4, 8]
-    auto* info = analysis.GetInfo(divide);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(divide);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, Divide_Failure_ZeroRHS_I32) {
@@ -12487,8 +12487,8 @@
 
     // Range of `divide` (`idx / idy`)
     // idx: [4, 16], idy: [0, 8]
-    auto* info = analysis.GetInfo(divide);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(divide);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, Divide_Failure_ZeroRHS_U32) {
@@ -12611,8 +12611,8 @@
 
     // Range of `divide` (`idx / idy`)
     // idx: [4u, 8u], idy: [0u, 2u]
-    auto* info = analysis.GetInfo(divide);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(divide);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, ShiftLeft_Success_U32) {
@@ -12736,10 +12736,10 @@
     // Range of `shiftLeft` (`idx << idy`)
     // idx: [4u, 8u], idy: [1u, 2u]
     // shiftLeft: [4u << 1u, 8u << 2u] = [8u, 32u]
-    auto* info = analysis.GetInfo(shiftLeft);
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& info = analysis.GetInfo(shiftLeft);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(8u, range.min_bound);
     EXPECT_EQ(32u, range.max_bound);
 }
@@ -12865,10 +12865,10 @@
     // Range of `shiftLeft` (`idx << idy`)
     // idx: [4, 8], idy: [1u, 2u]
     // shiftLeft: [4 << 1u, 8 << 2u] = [8, 32]
-    auto* info = analysis.GetInfo(shiftLeft);
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& info = analysis.GetInfo(shiftLeft);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(8, range.min_bound);
     EXPECT_EQ(32, range.max_bound);
 }
@@ -12994,10 +12994,10 @@
     // Range of `shiftLeft` (`idx << idy`)
     // idx: [0, 8], idy: [1u, 2u]
     // shiftLeft: [0 << 1u, 8 << 2u] = [0, 32]
-    auto* info = analysis.GetInfo(shiftLeft);
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& info = analysis.GetInfo(shiftLeft);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(0, range.min_bound);
     EXPECT_EQ(32, range.max_bound);
 }
@@ -13122,8 +13122,8 @@
 
     // Range of `shiftLeft` (`idx << idy`)
     // idx: [-1, 8], idy: [1u, 2u]
-    auto* info = analysis.GetInfo(shiftLeft);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(shiftLeft);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, ShiftLeft_Failure_U32_NoLessThan32) {
@@ -13246,8 +13246,8 @@
 
     // Range of `shiftLeft` (`idx << idy`)
     // idx: [4u, 8u], idy: [1u, 32u]
-    auto* info = analysis.GetInfo(shiftLeft);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(shiftLeft);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, ShiftLeft_Failure_I32_NoLessThan32) {
@@ -13370,8 +13370,8 @@
 
     // Range of `shiftLeft` (`idx << idy`)
     // idx: [0, 8], idy: [1u, 33u]
-    auto* info = analysis.GetInfo(shiftLeft);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(shiftLeft);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, ShiftLeft_Failure_U32_Overflow) {
@@ -13495,8 +13495,8 @@
     // Range of `shiftLeft` (`idx << idy`)
     // idx: [0u, 4u], idy: [1u, 30u]
     // 4 << 30 = 4294967296L > u32::kHighestValue
-    auto* info = analysis.GetInfo(shiftLeft);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(shiftLeft);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, ShiftLeft_Failure_U32_HighestValue_Overflow) {
@@ -13570,8 +13570,8 @@
 
     // Range of `shiftLeft` (`u32::HighestValue << idx`)
     // idx: [0u, 31u]
-    auto* info = analysis.GetInfo(shiftLeft);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(shiftLeft);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, ShiftLeft_Failure_I32_Overflow) {
@@ -13695,8 +13695,8 @@
     // Range of `shiftLeft` (`idx << idy`)
     // idx: [0, 4], idy: [1u, 29u]
     // 4 << 29 = 2147483648 > i32::kHighestValue
-    auto* info = analysis.GetInfo(shiftLeft);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(shiftLeft);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, ShiftLeft_Failure_I32_HighestValue_Overflow) {
@@ -13770,8 +13770,8 @@
 
     // Range of `shiftLeft` (`i32::HighestValue << idx`)
     // idx: [0u, 31u]
-    auto* info = analysis.GetInfo(shiftLeft);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(shiftLeft);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, ShiftRight_Success_U32_NonZero) {
@@ -13895,10 +13895,10 @@
     // Range of `shiftRight` (`idx >> idy`)
     // idx: [4u, 8u], idy: [1u, 2u]
     // shiftRight: [4u >> 2u, 8u >> 1u] = [1u, 4u]
-    auto* info = analysis.GetInfo(shiftRight);
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& info = analysis.GetInfo(shiftRight);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(1u, range.min_bound);
     EXPECT_EQ(4u, range.max_bound);
 }
@@ -14024,10 +14024,10 @@
     // Range of `shiftRight` (`idx >> idy`)
     // idx: [4, 8], idy: [1u, 2u]
     // shiftRight: [4 >> 2u, 8 >> 1u] = [1, 4]
-    auto* info = analysis.GetInfo(shiftRight);
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& info = analysis.GetInfo(shiftRight);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(1, range.min_bound);
     EXPECT_EQ(4, range.max_bound);
 }
@@ -14153,10 +14153,10 @@
     // Range of `shiftRight` (`idx >> idy`)
     // idx: [4u, 8u], idy: [1u, 4u]
     // shiftRight: [4u >> 4u, 8u >> 1u] = [0u, 4u]
-    auto* info = analysis.GetInfo(shiftRight);
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info->range));
-    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info->range);
+    const auto& info = analysis.GetInfo(shiftRight);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::UnsignedIntegerRange>(info.range));
+    const auto& range = std::get<IntegerRangeInfo::UnsignedIntegerRange>(info.range);
     EXPECT_EQ(0u, range.min_bound);
     EXPECT_EQ(4u, range.max_bound);
 }
@@ -14282,10 +14282,10 @@
     // Range of `shiftRight` (`idx >> idy`)
     // idx: [4, 8], idy: [4u, 5u]
     // shiftRight: [4 >> 5u, 8 >> 4u] = [0, 0]
-    auto* info = analysis.GetInfo(shiftRight);
-    ASSERT_NE(nullptr, info);
-    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info->range));
-    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info->range);
+    const auto& info = analysis.GetInfo(shiftRight);
+    ASSERT_TRUE(info.IsValid());
+    ASSERT_TRUE(std::holds_alternative<IntegerRangeInfo::SignedIntegerRange>(info.range));
+    const auto& range = std::get<IntegerRangeInfo::SignedIntegerRange>(info.range);
     EXPECT_EQ(0, range.min_bound);
     EXPECT_EQ(0, range.max_bound);
 }
@@ -14410,8 +14410,8 @@
 
     // Range of `shiftRight` (`idx >> idy`)
     // idx: [-4, 8], idy: [4u, 5u]
-    auto* info = analysis.GetInfo(shiftRight);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(shiftRight);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, ShiftRight_Failure_U32_NoLessThan32) {
@@ -14488,8 +14488,8 @@
 
     // Range of `shiftRight` (`u32::kHighestValue >> idx`)
     // idx: [4u, 32u]
-    auto* info = analysis.GetInfo(shiftRight);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(shiftRight);
+    ASSERT_FALSE(info.IsValid());
 }
 
 TEST_F(IR_IntegerRangeAnalysisTest, ShiftRight_Failure_I32_NoLessThan32) {
@@ -14566,8 +14566,8 @@
 
     // Range of `shiftRight` (`i32::kHighestValue >> idx`)
     // idx: [4, 32]
-    auto* info = analysis.GetInfo(shiftRight);
-    ASSERT_EQ(nullptr, info);
+    const auto& info = analysis.GetInfo(shiftRight);
+    ASSERT_FALSE(info.IsValid());
 }
 
 }  // namespace
diff --git a/src/tint/lang/core/ir/transform/robustness.cc b/src/tint/lang/core/ir/transform/robustness.cc
index f8e10a6..450270e 100644
--- a/src/tint/lang/core/ir/transform/robustness.cc
+++ b/src/tint/lang/core/ir/transform/robustness.cc
@@ -245,8 +245,8 @@
         }
 
         // Return true when we cannot get a valid range for `idx`.
-        const auto* integer_range = integer_range_analysis->GetInfo(idx);
-        if (!integer_range) {
+        const auto& integer_range = integer_range_analysis->GetInfo(idx);
+        if (!integer_range.IsValid()) {
             return true;
         }
 
@@ -258,11 +258,11 @@
 
         // Return true when `idx` may be negative or the upper bound of `idx` is greater than
         // `limit`.
-        if (std::holds_alternative<UnsignedIntegerRange>(integer_range->range)) {
-            UnsignedIntegerRange range = std::get<UnsignedIntegerRange>(integer_range->range);
+        if (std::holds_alternative<UnsignedIntegerRange>(integer_range.range)) {
+            UnsignedIntegerRange range = std::get<UnsignedIntegerRange>(integer_range.range);
             return range.max_bound > static_cast<uint64_t>(const_limit_value);
         } else {
-            SignedIntegerRange range = std::get<SignedIntegerRange>(integer_range->range);
+            SignedIntegerRange range = std::get<SignedIntegerRange>(integer_range.range);
             if (range.min_bound < 0) {
                 return true;
             }