[Mlir-commits] [mlir] [mlir] Refactor opaque properties to make them type-safe (PR #185157)

Fabian Mora llvmlistbot at llvm.org
Sat Mar 21 12:06:25 PDT 2026


fabianmcg wrote:

Here's how two possible solutions to the int cast problem:
```C++
#include "llvm/Support/Casting.h"
#include <cassert>
#include <cstdint>
#include <iostream>

//===----------------------------------------------------------------------===//
// PropRef and TypedPropRef
//===----------------------------------------------------------------------===//

enum class TypeID { Int32, Int64 };

struct PropRef {
  void *Ptr;
  TypeID Type;

  PropRef(int32_t &ref) : Ptr(&ref), Type(TypeID::Int32) {}
  PropRef(int64_t &ref) : Ptr(&ref), Type(TypeID::Int64) {}

  void *getPtr() const { return Ptr; }
  TypeID getTypeID() const { return Type; }
};

template <typename T> struct TypedPropRef {
  T *Ptr = nullptr;

  TypedPropRef() = default;
  explicit TypedPropRef(T &ref) : Ptr(&ref) {}

  explicit operator bool() const { return Ptr != nullptr; }
  T &getRef() { return *Ptr; }
  const T &getRef() const { return *Ptr; }
};

//===----------------------------------------------------------------------===//
// LLVM traits
//===----------------------------------------------------------------------===//

template <> struct llvm::simplify_type<const PropRef> {
  using SimpleType = PropRef;
  static PropRef &getSimplifiedValue(const PropRef &v) {
    return const_cast<PropRef &>(v);
  }
};

template <>
struct llvm::CastInfo<TypedPropRef<int32_t>, PropRef>
    : llvm::CastIsPossible<TypedPropRef<int32_t>, PropRef>,
      llvm::DefaultDoCastIfPossible<
          TypedPropRef<int32_t>, PropRef,
          llvm::CastInfo<TypedPropRef<int32_t>, PropRef>> {
  static bool isPossible(const PropRef &r) {
    return r.getTypeID() == TypeID::Int32;
  }
  static TypedPropRef<int32_t> castFailed() { return TypedPropRef<int32_t>{}; }
  static TypedPropRef<int32_t> doCast(const PropRef &r) {
    return TypedPropRef<int32_t>(*static_cast<int32_t *>(r.getPtr()));
  }
};

template <>
struct llvm::CastInfo<TypedPropRef<int64_t>, PropRef>
    : llvm::CastIsPossible<TypedPropRef<int64_t>, PropRef>,
      llvm::DefaultDoCastIfPossible<
          TypedPropRef<int64_t>, PropRef,
          llvm::CastInfo<TypedPropRef<int64_t>, PropRef>> {
  static bool isPossible(const PropRef &r) {
    return r.getTypeID() == TypeID::Int64;
  }
  static TypedPropRef<int64_t> castFailed() { return TypedPropRef<int64_t>{}; }
  static TypedPropRef<int64_t> doCast(const PropRef &r) {
    return TypedPropRef<int64_t>(*static_cast<int64_t *>(r.getPtr()));
  }
};

template <>
struct llvm::CastInfo<int32_t &, PropRef>
    : llvm::CastIsPossible<int32_t &, PropRef> {
  static bool isPossible(const PropRef &r) {
    return r.getTypeID() == TypeID::Int32;
  }
  static int32_t &doCast(const PropRef &r) {
    return *static_cast<int32_t *>(r.getPtr());
  }
  static int32_t &castFailed() {
    assert(false && "cast to int32_t& failed");
    return *static_cast<int32_t *>(nullptr);
  }
  static int32_t &doCastIfPossible(const PropRef &r) {
    assert(isPossible(r) && "cast to int32_t& failed");
    return doCast(r);
  }
};

template <>
struct llvm::CastInfo<int64_t &, PropRef>
    : llvm::CastIsPossible<int64_t &, PropRef> {
  static bool isPossible(const PropRef &r) {
    return r.getTypeID() == TypeID::Int64;
  }
  static int64_t &doCast(const PropRef &r) {
    return *static_cast<int64_t *>(r.getPtr());
  }
  static int64_t &castFailed() {
    assert(false && "cast to int64_t& failed");
    return *static_cast<int64_t *>(nullptr);
  }
  static int64_t &doCastIfPossible(const PropRef &r) {
    assert(isPossible(r) && "cast to int64_t& failed");
    return doCast(r);
  }
};

//===----------------------------------------------------------------------===//
// Test
//===----------------------------------------------------------------------===//

int main() {
  std::cout << "=== Testing PropRef ===" << std::endl;
  // Declare some values
  int32_t myVal32 = 42;
  int64_t myVal64 = 12345678901234LL;

  // Create PropRefs
  PropRef ref32(myVal32);
  PropRef ref64(myVal64);

  // Cast test to TypedPropRef<int32_t> and TypedPropRef<int64_t>
  TypedPropRef<int32_t> mpV32 = llvm::dyn_cast<TypedPropRef<int32_t>>(ref32);
  assert(mpV32 && mpV32.getRef() == 42);
  TypedPropRef<int64_t> mpV64 = llvm::dyn_cast<TypedPropRef<int64_t>>(ref64);
  assert(mpV64 && mpV64.getRef() == 12345678901234LL);
  TypedPropRef<int64_t> bad = llvm::dyn_cast<TypedPropRef<int64_t>>(ref32);
  assert(!bad && "wrong type should yield empty");

  // Cast test to int32_t& and int64_t&
  int32_t &r32 = llvm::cast<int32_t &>(ref32);
  int64_t &r64 = llvm::cast<int64_t &>(ref64);
  r32 = 100;
  assert(myVal32 == 100 && "cast<int32_t&> failed");
  assert(myVal64 == 12345678901234LL && "cast<int64_t&> failed");

  std::cout << "All tests passed!" << std::endl;
  return 0;
}
```

> The circumstances include things like OperationState, which needs a null properties until you getOrAddProperties<T>() at which point space gets allocated and the type gets locked in.

That should be covered by the default constructor.

https://github.com/llvm/llvm-project/pull/185157


More information about the Mlir-commits mailing list