[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