current commit
260826.7 · ConstraintAnalysis: Fix mixed signed/unsigned comparisons (#9045)
/*
 * Copyright 2026 WebAssembly Community Group participants
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef wasm_support_span_h
#define wasm_support_span_h

#include <algorithm>
#include <cassert>
#include <cstdint>
#include <iostream>
#include <limits>

#include "support/inplace_vector.h"

namespace wasm {

// A span of values.
//
// Span{min, max} means [min, max], inclusive of both sides. To represent an
// empty span, we use min > max.
template<typename T> struct Span {
  static constexpr T Min = std::numeric_limits<T>::lowest();
  static constexpr T Max = std::numeric_limits<T>::max();

  T min = Min;
  T max = Max;

  constexpr Span() = default;
  constexpr Span(T min, T max) : min(min), max(max) {}

  // Set a single value as possible.
  void set(T value) { min = max = value; }

  // To represent an empty span, we use min > max, an impossible span.
  void setEmpty() {
    *this = empty();
    assert(isEmpty());
  }

  bool isEmpty() const { return min > max; }

  static Span<T> empty() { return Span{Max, Min}; }

  void setFull() {
    *this = Span();
    assert(isFull());
  }

  bool isFull() const { return min == Min && max == Max; }

  static Span<T> full() { return Span{}; }

  // Intersect this with another span, returning a (possibly empty) span.
  Span<T> intersection(const Span& other) const {
    if (isEmpty() || other.isEmpty()) {
      return empty();
    }
    return Span<T>{std::max(min, other.min), std::min(max, other.max)};
  }

  // Checks whether two spans have any overlap at all.
  bool hasOverlap(const Span& other) const {
    return !intersection(other).isEmpty();
  }

  // Check whether we contain another span (possibly being equal).
  bool contains(const Span& other) const {
    return intersection(other) == other;
  }

  bool operator==(const Span& other) const {
    if (isEmpty()) {
      return other.isEmpty();
    }
    return !other.isEmpty() && min == other.min && max == other.max;
  }
  bool operator!=(const Span& other) const { return !(*this == other); }
};

// A union of spans, which we assume are disjoint.
template<typename T, size_t N>
struct Spans : public inplace_vector<Span<T>, N> {
  constexpr Spans() = default;

  // Initialize with Spans.
  Spans(std::initializer_list<Span<T>> init) {
    for (const auto& span : init) {
      this->push_back(span);
    }
  }

  bool hasOverlap(const Spans<T, N>& other) const {
    // There is overlap if any of our spans overlaps with any of other's.
    for (const auto& span : *this) {
      for (const auto& otherSpan : other) {
        if (span.hasOverlap(otherSpan)) {
          return true;
        }
      }
    }
    return false;
  }

  bool contains(const Spans<T, N>& other) const {
    // We contain other if each of their spans is contained in us.
    for (const auto& otherSpan : other) {
      // Because our spans are assumed to be disjoint, exactly one of our
      // spans must contain otherSpan.
      bool found = false;
      for (const auto& span : *this) {
        if (span.contains(otherSpan)) {
          found = true;
          break;
        }
      }
      if (!found) {
        return false;
      }
    }
    return true;
  }
};

// A useful set of 2 spans that can contain any integer value. 2 spans is enough
// to contain spans for any inequality, signed or unsigned: we represent numbers
// as unsigned internally, and so e.g. signed x <= 10 ends up as two disjoint
// spans, [0..10] and [2^31..MAX_INT].
using SpansU2 = Spans<uint64_t, 2>;

template<typename T>
inline std::ostream& operator<<(std::ostream& os, const Span<T>& span) {
  if (span.isEmpty()) {
    return os << "[empty]";
  }
  return os << '[' << span.min << ", " << span.max << ']';
}

template<typename T, size_t N>
inline std::ostream& operator<<(std::ostream& os, const Spans<T, N>& spans) {
  if (spans.empty()) {
    return os << "{empty}";
  }
  os << '{';
  for (size_t i = 0; i < spans.size(); ++i) {
    if (i > 0) {
      os << ", ";
    }
    os << spans[i];
  }
  return os << '}';
}

} // namespace wasm

#endif // wasm_support_span_h