Repository: NVIDIA/cuda-quantum
Pull request #2805: Fix constant folding of booleans casted to integers
Author: login: annagrin
Labels: name: bug fix
description: To be listed under Bug Fixes in the release notes
Created: 2025-04-10T21:45:58Z | Merged: 2025-05-23T15:22:54Z

### Description
Fix constant folding of casts of integers.

Using the MLIR below
```mlir
%0 = arith.constant <c> : i8
%1 = cc.cast unsigned %0: i32
```
The constant folding creates the following code without changing `<c>`:
```mlir
%1 = arith.constant <c>: i32
```

This leads to incorrect values in constants when constant folding is invoked, for example,
`(unsigned)(true)` is currently converted to a `-1` integer constant.  

See added tests in `targettests/execution/cast.cpp` for more examples.

This PR updates constant folding to cast the source of the cast instruction to its original type before folding into a constant of a new type.

Example:

```c++
struct testConstBool {
  auto operator()() __qpu__ {
    cudaq::qubit q;
    unsigned i0 = (unsigned)(true);
    if (i0 == 1) {
      x(q);
    }
  }
};

int main() {
  auto counts = cudaq::sample(testConstBool{});
  counts.dump();
}
```

Quake before the fix:
```mlir
func.func @__nvqpp__mlirgen__testConstBool () attributes {"cudaq-entrypoint", "cudaq-kernel"} {
    %c-1_i32 = arith.constant -1 : i32
    %c1_i32 = arith.constant 1 : i32
    %0 = quake.alloca !quake.ref
    %1 = cc.alloca i32
    cc.store %c-1_i32, %1 : !cc.ptr<i32>
    %2 = cc.load %1 : !cc.ptr<i32>
    %3 = arith.cmpi eq, %2, %c1_i32 : i32
    cc.if(%3) {
      quake.x %0 : (!quake.ref) -> ()
    }
    return
  }
```

Quake after the fix:
```mlir
func.func @__nvqpp__mlirgen__testConstBool () attributes {"cudaq-entrypoint", "cudaq-kernel"} {
    %c1_i32 = arith.constant 1 : i32
    %0 = quake.alloca !quake.ref
    %1 = cc.alloca i32
    cc.store %c1_i32, %1 : !cc.ptr<i32>
    %2 = cc.load %1 : !cc.ptr<i32>
    %3 = arith.cmpi eq, %2, %c1_i32 : i32
    cc.if(%3) {
      quake.x %0 : (!quake.ref) -> ()
    }
    return
  }
```

Result before the fix:
`{ 0:0000 }`

Result after the fix:
`{ 1:0000 }`

Closes: https://github.com/NVIDIA/cuda-quantum/issues/2804






Diff:
diff --git a/lib/Optimizer/Dialect/CC/CCOps.cpp b/lib/Optimizer/Dialect/CC/CCOps.cpp
index 0a27d03a4fb..675c4395a5a 100644
--- a/lib/Optimizer/Dialect/CC/CCOps.cpp
+++ b/lib/Optimizer/Dialect/CC/CCOps.cpp
@@ -214,8 +214,23 @@ OpFoldResult cudaq::cc::CastOp::fold(FoldAdaptor adaptor) {
       auto val = attr.getInt();
       if (isa<IntegerType>(ty)) {
         auto width = ty.getIntOrFloatBitWidth();
+        auto srcTy = getValue().getType();
+        auto srcWidth = srcTy.getIntOrFloatBitWidth();
+
+        if (getZint()) {
+          // Zero-extend to get the original integer value.
+          if (srcWidth < 64)
+            val &= ((1UL << srcWidth) - 1);
+        }
+
+        if (width == 1) {
+          bool v = val != 0;
+          return builder.create<arith::ConstantIntOp>(loc, v, width)
+              .getResult();
+        }
         return builder.create<arith::ConstantIntOp>(loc, val, width)
             .getResult();
+
       } else if (ty == fltTy) {
         if (getZint()) {
           APFloat fval(static_cast<float>(static_cast<std::uint64_t>(val)));
diff --git a/targettests/execution/cast.cpp b/targettests/execution/cast.cpp
new file mode 100644
index 00000000000..a394a27850a
--- /dev/null
+++ b/targettests/execution/cast.cpp
@@ -0,0 +1,732 @@
+/*******************************************************************************
+ * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates.                  *
+ * All rights reserved.                                                        *
+ *                                                                             *
+ * This source code and the accompanying materials are made available under    *
+ * the terms of the Apache License 2.0 which accompanies this distribution.    *
+ ******************************************************************************/
+
+// clang-format off
+// RUN: nvq++ %cpp_std --enable-mlir %s -o %t && %t | FileCheck %s
+// clang-format on
+
+#include <cudaq.h>
+#include <iostream>
+#include <string>
+#include <vector>
+
+template <typename S, typename D>
+std::string cast_and_compare(S val, D expected) {
+  return ((D)val) == expected ? "1" : "0";
+}
+
+struct testBool {
+  auto operator()() __qpu__ {
+    cudaq::qubit qb4;
+    bool b4 = (std::int32_t)(bool)(4);
+    if (b4 == 1) {
+      x(qb4);
+    }
+
+    cudaq::qubit qb_4;
+    bool b_4 = (std::int32_t)(bool)(-2);
+    if (b_4 == 1) {
+      x(qb_4);
+    }
+
+    cudaq::qubit qb;
+    bool b = (bool)(true);
+    if (b == true) {
+      x(qb);
+    }
+
+    cudaq::qubit qu8;
+    std::uint8_t u8 = (std::uint8_t)(true);
+    if (u8 == 1) {
+      x(qu8);
+    }
+    cudaq::qubit qi8;
+    std::int8_t i8 = (std::int8_t)(true);
+    if (i8 == 1) {
+      x(qi8);
+    }
+
+    cudaq::qubit qu16;
+    std::uint16_t u16 = (std::uint16_t)(true);
+    if (u16 == 1) {
+      x(qu16);
+    }
+    cudaq::qubit qi16;
+    std::int16_t i16 = (std::int16_t)(true);
+    if (i16 == 1) {
+      x(qi16);
+    }
+
+    cudaq::qubit qu32;
+    std::uint32_t u32 = (std::uint32_t)(true);
+    if (u32 == 1) {
+      x(qu32);
+    }
+    cudaq::qubit qi32;
+    std::int32_t i32 = (std::int32_t)(true);
+    if (i32 == 1) {
+      x(qi32);
+    }
+
+    cudaq::qubit qu64;
+    std::uint64_t u64 = (std::uint64_t)(true);
+    if (u64 == 1) {
+      x(qu64);
+    }
+    cudaq::qubit qi64;
+    std::int64_t i64 = (std::int64_t)(true);
+    if (i64 == 1) {
+      x(qi64);
+    }
+  }
+};
+
+struct testInt8 {
+  auto operator()() __qpu__ {
+    cudaq::qubit qb;
+    bool b = (bool)((std::int8_t)-1);
+    if (b == true) {
+      x(qb);
+    }
+
+    cudaq::qubit qu8;
+    std::uint8_t u8 = (std::uint8_t)((std::int8_t)-1);
+    if (u8 == 255) {
+      x(qu8);
+    }
+    cudaq::qubit qi8;
+    std::int8_t i8 = (std::int8_t)((std::int8_t)-1);
+    if (i8 == -1) {
+      x(qi8);
+    }
+
+    cudaq::qubit qu16;
+    std::uint16_t u16 = (std::uint16_t)((std::int8_t)-1);
+    if (u16 == 65535) {
+      x(qu16);
+    }
+    cudaq::qubit qi16;
+    std::int16_t i16 = (std::int16_t)((std::int8_t)-1);
+    if (i16 == -1) {
+      x(qi16);
+    }
+
+    cudaq::qubit qu32;
+    std::uint32_t u32 = (std::uint32_t)((std::int8_t)-1);
+    if (u32 == 4294967295) {
+      x(qu32);
+    }
+    cudaq::qubit qi32;
+    std::int32_t i32 = (std::int32_t)((std::int8_t)-1);
+    if (i32 == -1) {
+      x(qi32);
+    }
+
+    cudaq::qubit qu64;
+    std::uint64_t u64 = (std::uint64_t)((std::int8_t)-1);
+    if (u64 == 18446744073709551615UL) {
+      x(qu64);
+    }
+    cudaq::qubit qi64;
+    std::int64_t i64 = (std::int64_t)((std::int8_t)-1);
+    if (i64 == -1) {
+      x(qi64);
+    }
+  }
+};
+
+struct testInt16 {
+  auto operator()() __qpu__ {
+    cudaq::qubit qb;
+    bool b = (bool)((std::int16_t)-1);
+    if (b == true) {
+      x(qb);
+    }
+
+    cudaq::qubit qu8;
+    std::uint8_t u8 = (std::uint8_t)((std::int16_t)-1);
+    if (u8 == 255) {
+      x(qu8);
+    }
+    cudaq::qubit qi8;
+    std::int8_t i8 = (std::int8_t)((std::int16_t)-1);
+    if (i8 == -1) {
+      x(qi8);
+    }
+
+    cudaq::qubit qu16;
+    std::uint16_t u16 = (std::uint16_t)((std::int16_t)-1);
+    if (u16 == 65535) {
+      x(qu16);
+    }
+    cudaq::qubit qi16;
+    std::int16_t i16 = (std::int16_t)((std::int16_t)-1);
+    if (i16 == -1) {
+      x(qi16);
+    }
+
+    cudaq::qubit qu32;
+    std::uint32_t u32 = (std::uint32_t)((std::int16_t)-1);
+    if (u32 == 4294967295) {
+      x(qu32);
+    }
+    cudaq::qubit qi32;
+    std::int32_t i32 = (std::int32_t)((std::int16_t)-1);
+    if (i32 == -1) {
+      x(qi32);
+    }
+
+    cudaq::qubit qu64;
+    std::uint64_t u64 = (std::uint64_t)((std::int16_t)-1);
+    if (u64 == 18446744073709551615UL) {
+      x(qu64);
+    }
+    cudaq::qubit qi64;
+    std::int64_t i64 = (std::int64_t)((std::int16_t)-1);
+    if (i64 == -1) {
+      x(qi64);
+    }
+  }
+};
+
+struct testInt32 {
+  auto operator()() __qpu__ {
+    cudaq::qubit qb;
+    bool b = (bool)((std::int32_t)-1);
+    if (b == true) {
+      x(qb);
+    }
+
+    cudaq::qubit qu8;
+    std::uint8_t u8 = (std::uint8_t)((std::int32_t)-1);
+    if (u8 == 255) {
+      x(qu8);
+    }
+    cudaq::qubit qi8;
+    std::int8_t i8 = (std::int8_t)((std::int32_t)-1);
+    if (i8 == -1) {
+      x(qi8);
+    }
+
+    cudaq::qubit qu16;
+    std::uint16_t u16 = (std::uint16_t)((std::int32_t)-1);
+    if (u16 == 65535) {
+      x(qu16);
+    }
+    cudaq::qubit qi16;
+    std::int16_t i16 = (std::int16_t)((std::int32_t)-1);
+    if (i16 == -1) {
+      x(qi16);
+    }
+
+    cudaq::qubit qu32;
+    std::uint32_t u32 = (std::uint32_t)((std::int32_t)-1);
+    if (u32 == 4294967295) {
+      x(qu32);
+    }
+    cudaq::qubit qi32;
+    std::int32_t i32 = (std::int32_t)((std::int32_t)-1);
+    if (i32 == -1) {
+      x(qi32);
+    }
+
+    cudaq::qubit qu64;
+    std::uint64_t u64 = (std::uint64_t)((std::int32_t)-1);
+    if (u64 == 18446744073709551615UL) {
+      x(qu64);
+    }
+    cudaq::qubit qi64;
+    std::int64_t i64 = (std::int64_t)((std::int32_t)-1);
+    if (i64 == -1) {
+      x(qi64);
+    }
+  }
+};
+
+struct testInt64 {
+  auto operator()() __qpu__ {
+    cudaq::qubit qb;
+    bool b = (bool)((std::int64_t)-1);
+    if (b == true) {
+      x(qb);
+    }
+
+    cudaq::qubit qu8;
+    std::uint8_t u8 = (std::uint8_t)((std::int64_t)-1);
+    if (u8 == 255) {
+      x(qu8);
+    }
+    cudaq::qubit qi8;
+    std::int8_t i8 = (std::int8_t)((std::int64_t)-1);
+    if (i8 == -1) {
+      x(qi8);
+    }
+
+    cudaq::qubit qu16;
+    std::uint16_t u16 = (std::uint16_t)((std::int64_t)-1);
+    if (u16 == 65535) {
+      x(qu16);
+    }
+    cudaq::qubit qi16;
+    std::int16_t i16 = (std::int16_t)((std::int64_t)-1);
+    if (i16 == -1) {
+      x(qi16);
+    }
+
+    cudaq::qubit qu32;
+    std::uint32_t u32 = (std::uint32_t)((std::int64_t)-1);
+    if (u32 == 4294967295) {
+      x(qu32);
+    }
+    cudaq::qubit qi32;
+    std::int32_t i32 = (std::int32_t)((std::int64_t)-1);
+    if (i32 == -1) {
+      x(qi32);
+    }
+
+    cudaq::qubit qu64;
+    std::uint64_t u64 = (std::uint64_t)((std::int64_t)-1);
+    if (u64 == 18446744073709551615UL) {
+      x(qu64);
+    }
+    cudaq::qubit qi64;
+    std::int64_t i64 = (std::int64_t)((std::int64_t)-1);
+    if (i64 == -1) {
+      x(qi64);
+    }
+  }
+};
+
+void printCounts(cudaq::sample_result &result) {
+  std::vector<std::string> values{};
+  for (auto &&[bits, counts] : result) {
+    values.push_back(bits);
+  }
+
+  std::sort(values.begin(), values.end());
+  for (auto &&bits : values) {
+    std::cout << bits << std::endl;
+  }
+}
+
+struct testConsecutiveCasts {
+  auto operator()() __qpu__ {
+    cudaq::qubit qi32;
+    std::int64_t i32 = (std::int32_t)(std::int64_t)((std::int16_t)-1);
+    if (i32 == -1) {
+      x(qi32);
+    }
+
+    cudaq::qubit qu32;
+    std::int64_t u32 = (std::int32_t)(std::uint64_t)((std::int16_t)-1);
+    if (u32 == -1) {
+      x(qu32);
+    }
+
+    cudaq::qubit qi64;
+    std::int64_t i64 = (std::int64_t)(std::int16_t)((std::int32_t)-1);
+    if (i64 == -1) {
+      x(qi64);
+    }
+
+    cudaq::qubit qu64;
+    std::int64_t u64 = (std::int64_t)(std::uint16_t)((std::int32_t)-1);
+    if (u64 == 65535) {
+      x(qu64);
+    }
+  }
+};
+
+struct testUnsignedTruncation {
+  auto operator()() __qpu__ {
+    cudaq::qubit qi32;
+    std::uint8_t i32 = (std::uint8_t)(std::uint32_t)(-1);
+    if (i32 == 255) {
+      x(qi32);
+    }
+
+    cudaq::qubit qi64;
+    std::uint32_t i64 = (std::uint32_t)(std::uint64_t)(-1);
+    if (i64 == 4294967295) {
+      x(qi64);
+    }
+  }
+};
+
+struct testUnsignedExtension {
+  auto operator()() __qpu__ {
+    cudaq::qubit qi8;
+    std::uint32_t i8 = (std::uint32_t)(std::uint8_t)(-1);
+    if (i8 == 255) {
+      x(qi8);
+    }
+
+    cudaq::qubit qi32;
+    std::uint64_t i32 = (std::uint64_t)(std::uint32_t)(-1);
+    if (i32 == 4294967295) {
+      x(qi32);
+    }
+  }
+};
+
+struct testSignedToUnsignedTruncation {
+  auto operator()() __qpu__ {
+    cudaq::qubit qi32;
+    std::uint8_t i32 = (std::uint8_t)(std::int32_t)(-1);
+    if (i32 == 255) {
+      x(qi32);
+    }
+
+    cudaq::qubit qi64;
+    std::uint32_t i64 = (std::uint32_t)(std::int64_t)(-1);
+    if (i64 == 4294967295) {
+      x(qi64);
+    }
+  }
+};
+
+struct testSignedToUnsignedExtension {
+  auto operator()() __qpu__ {
+    cudaq::qubit qi8;
+    std::uint32_t i8 = (std::uint32_t)(std::int8_t)(-1);
+    if (i8 == 4294967295) {
+      x(qi8);
+    }
+
+    cudaq::qubit qi32;
+    std::uint64_t i32 = (std::uint64_t)(std::int32_t)(-1);
+    if (i32 == 18446744073709551615UL) {
+      x(qi32);
+    }
+  }
+};
+
+struct testUnsignedToSignedTruncation {
+  auto operator()() __qpu__ {
+    cudaq::qubit qi32;
+    std::int8_t i32 = (std::int8_t)(std::uint32_t)(-1);
+    if (i32 == (std::int8_t)(-1)) {
+      x(qi32);
+    }
+
+    cudaq::qubit qi64;
+    std::int32_t i64 = (std::int32_t)(std::uint64_t)(-1);
+    if (i64 == std::int32_t(-1)) {
+      x(qi64);
+    }
+  }
+};
+
+struct testUnsignedToSignedExtension {
+  auto operator()() __qpu__ {
+    cudaq::qubit qi8;
+    std::int32_t i8 = (std::int32_t)(std::uint8_t)(-1);
+    if (i8 == 255) {
+      x(qi8);
+    }
+
+    cudaq::qubit qi32;
+    std::int64_t i32 = (std::int64_t)(std::uint32_t)(-1);
+    if (i32 == 4294967295) {
+      x(qi32);
+    }
+  }
+};
+
+int main() {
+  {
+    printf("*** Test casts from bool ***\n");
+    std::string expected = "";
+    expected += cast_and_compare<std::int64_t, bool>(4, 4);
+    expected += cast_and_compare<std::int64_t, bool>(-4, -4);
+
+    expected += cast_and_compare<bool, bool>(true, 1);
+
+    expected += cast_and_compare<bool, std::uint8_t>(true, 1);
+    expected += cast_and_compare<bool, std::int8_t>(true, 1);
+
+    expected += cast_and_compare<bool, std::uint16_t>(true, 1);
+    expected += cast_and_compare<bool, std::int16_t>(true, 1);
+
+    expected += cast_and_compare<bool, std::uint32_t>(true, 1);
+    expected += cast_and_compare<bool, std::int32_t>(true, 1);
+
+    expected += cast_and_compare<bool, std::uint64_t>(true, 1);
+    expected += cast_and_compare<bool, std::int64_t>(true, 1);
+
+    printf("Expected: %s\n", expected.c_str());
+
+    auto counts = cudaq::sample(testBool{});
+    printf("Actual:   ");
+    printCounts(counts);
+  }
+
+  {
+    printf("*** Test casts from int8 ***\n");
+    std::string expected = "";
+    expected += cast_and_compare<std::int8_t, bool>(
+        -1, std::numeric_limits<bool>::max());
+
+    expected += cast_and_compare<std::int8_t, std::uint8_t>(
+        -1, std::numeric_limits<std::uint8_t>::max());
+    expected += cast_and_compare<std::int8_t, std::int8_t>(-1, -1);
+
+    expected += cast_and_compare<std::int8_t, std::uint16_t>(
+        -1, std::numeric_limits<std::uint16_t>::max());
+    expected += cast_and_compare<std::int8_t, std::int16_t>(-1, -1);
+
+    expected += cast_and_compare<std::int8_t, std::uint32_t>(
+        -1, std::numeric_limits<std::uint32_t>::max());
+    expected += cast_and_compare<std::int8_t, std::int32_t>(-1, -1);
+
+    expected += cast_and_compare<std::int8_t, std::uint64_t>(
+        -1, std::numeric_limits<std::uint64_t>::max());
+    expected += cast_and_compare<std::int8_t, std::int64_t>(-1, -1);
+
+    printf("Expected: %s\n", expected.c_str());
+
+    auto counts = cudaq::sample(testInt8{});
+    printf("Actual:   ");
+    printCounts(counts);
+  }
+
+  {
+    printf("*** Test casts from int16 ***\n");
+    std::string expected = "";
+    expected += cast_and_compare<std::int16_t, bool>(
+        -1, std::numeric_limits<bool>::max());
+
+    expected += cast_and_compare<std::int16_t, std::uint8_t>(
+        -1, std::numeric_limits<std::uint8_t>::max());
+    expected += cast_and_compare<std::int16_t, std::int8_t>(-1, -1);
+
+    expected += cast_and_compare<std::int16_t, std::uint16_t>(
+        -1, std::numeric_limits<std::uint16_t>::max());
+    expected += cast_and_compare<std::int16_t, std::int16_t>(-1, -1);
+
+    expected += cast_and_compare<std::int16_t, std::uint32_t>(
+        -1, std::numeric_limits<std::uint32_t>::max());
+    expected += cast_and_compare<std::int16_t, std::int32_t>(-1, -1);
+
+    expected += cast_and_compare<std::int16_t, std::uint64_t>(
+        -1, std::numeric_limits<std::uint64_t>::max());
+    expected += cast_and_compare<std::int16_t, std::int64_t>(-1, -1);
+
+    printf("Expected: %s\n", expected.c_str());
+
+    auto counts = cudaq::sample(testInt16{});
+    printf("Actual:   ");
+    printCounts(counts);
+  }
+
+  {
+    printf("*** Test casts from int32 ***\n");
+    std::string expected = "";
+    expected += cast_and_compare<std::int32_t, bool>(
+        -1, std::numeric_limits<bool>::max());
+
+    expected += cast_and_compare<std::int32_t, std::uint8_t>(
+        -1, std::numeric_limits<std::uint8_t>::max());
+    expected += cast_and_compare<std::int32_t, std::int8_t>(-1, -1);
+
+    expected += cast_and_compare<std::int32_t, std::uint16_t>(
+        -1, std::numeric_limits<std::uint16_t>::max());
+    expected += cast_and_compare<std::int32_t, std::int16_t>(-1, -1);
+
+    expected += cast_and_compare<std::int32_t, std::uint32_t>(
+        -1, std::numeric_limits<std::uint32_t>::max());
+    expected += cast_and_compare<std::int32_t, std::int32_t>(-1, -1);
+
+    expected += cast_and_compare<std::int32_t, std::uint64_t>(
+        -1, std::numeric_limits<std::uint64_t>::max());
+    expected += cast_and_compare<std::int32_t, std::int64_t>(-1, -1);
+
+    printf("Expected: %s\n", expected.c_str());
+
+    auto counts = cudaq::sample(testInt32{});
+    printf("Actual:   ");
+    printCounts(counts);
+  }
+
+  {
+    printf("*** Test casts from int64 ***\n");
+    std::string expected = "";
+    expected += cast_and_compare<std::int64_t, bool>(
+        -1, std::numeric_limits<bool>::max());
+
+    expected += cast_and_compare<std::int64_t, std::uint8_t>(
+        -1, std::numeric_limits<std::uint8_t>::max());
+    expected += cast_and_compare<std::int64_t, std::int8_t>(-1, -1);
+
+    expected += cast_and_compare<std::int64_t, std::uint16_t>(
+        -1, std::numeric_limits<std::uint16_t>::max());
+    expected += cast_and_compare<std::int64_t, std::int16_t>(-1, -1);
+
+    expected += cast_and_compare<std::int64_t, std::uint32_t>(
+        -1, std::numeric_limits<std::uint32_t>::max());
+    expected += cast_and_compare<std::int64_t, std::int32_t>(-1, -1);
+
+    expected += cast_and_compare<std::int64_t, std::uint64_t>(
+        -1, std::numeric_limits<std::uint64_t>::max());
+    expected += cast_and_compare<std::int64_t, std::int64_t>(-1, -1);
+
+    printf("Expected: %s\n", expected.c_str());
+
+    auto counts = cudaq::sample(testInt64{});
+    printf("Actual:   ");
+    printCounts(counts);
+  }
+
+  {
+    printf("*** Test consecutive casts ***\n");
+    std::string expected = "";
+    expected +=
+        cast_and_compare<std::int64_t, std::int32_t>((std::int16_t)-1, -1);
+    expected +=
+        cast_and_compare<std::uint64_t, std::int32_t>((std::int16_t)-1, -1);
+
+    expected +=
+        cast_and_compare<std::int16_t, std::int64_t>((std::int32_t)-1, -1);
+    expected += cast_and_compare<std::uint16_t, std::int64_t>(
+        (std::int32_t)-1, std::numeric_limits<std::uint16_t>::max());
+
+    printf("Expected: %s\n", expected.c_str());
+
+    auto counts = cudaq::sample(testConsecutiveCasts{});
+    printf("Actual:   ");
+    printCounts(counts);
+  }
+
+  {
+    printf("*** Test unsigned truncation ***\n");
+    std::string expected = "";
+    expected += cast_and_compare<std::uint32_t, std::uint8_t>(-1, 255);
+    expected += cast_and_compare<std::uint64_t, std::uint32_t>(-1, 4294967295);
+
+    printf("Expected: %s\n", expected.c_str());
+
+    auto counts = cudaq::sample(testUnsignedTruncation{});
+    printf("Actual:   ");
+    printCounts(counts);
+  }
+
+  {
+    printf("*** Test unsigned extension ***\n");
+    std::string expected = "";
+    expected += cast_and_compare<std::uint8_t, std::uint32_t>(-1, 255);
+    expected += cast_and_compare<std::uint32_t, std::uint64_t>(-1, 4294967295);
+
+    printf("Expected: %s\n", expected.c_str());
+
+    auto counts = cudaq::sample(testUnsignedExtension{});
+    printf("Actual:   ");
+    printCounts(counts);
+  }
+
+  {
+    printf("*** Test signed to unsigned truncation ***\n");
+    std::string expected = "";
+    expected += cast_and_compare<std::int32_t, std::uint8_t>(-1, 255);
+    expected += cast_and_compare<std::int64_t, std::uint32_t>(-1, 4294967295);
+
+    printf("Expected: %s\n", expected.c_str());
+    auto counts = cudaq::sample(testSignedToUnsignedTruncation{});
+    printf("Actual:   ");
+    printCounts(counts);
+  }
+
+  {
+    printf("*** Test signed to unsigned extension ***\n");
+    std::string expected = "";
+    expected += cast_and_compare<std::int8_t, std::uint32_t>(-1, 4294967295);
+    expected += cast_and_compare<std::int32_t, std::uint64_t>(
+        -1, 18446744073709551615UL);
+
+    printf("Expected: %s\n", expected.c_str());
+
+    auto counts = cudaq::sample(testSignedToUnsignedExtension{});
+    printf("Actual:   ");
+    printCounts(counts);
+  }
+
+  {
+    printf("*** Test unsigned to signed truncation ***\n");
+    std::string expected = "";
+    expected += cast_and_compare<std::int32_t, std::uint8_t>(-1, 255);
+    expected += cast_and_compare<std::int64_t, std::uint32_t>(-1, 4294967295);
+
+    printf("Expected: %s\n", expected.c_str());
+
+    auto counts = cudaq::sample(testSignedToUnsignedTruncation{});
+    printf("Actual:   ");
+    printCounts(counts);
+  }
+
+  {
+    printf("*** Test unsigned to signed extension ***\n");
+    std::string expected = "";
+    expected += cast_and_compare<std::uint8_t, std::int32_t>(-1, 255);
+    expected += cast_and_compare<std::uint32_t, std::int64_t>(-1, 4294967295);
+
+    printf("Expected: %s\n", expected.c_str());
+
+    auto counts = cudaq::sample(testUnsignedToSignedExtension{});
+    printf("Actual:   ");
+    printCounts(counts);
+  }
+
+  return 0;
+}
+
+// CHECK: *** Test casts from bool ***
+// CHECK: Expected: 11111111111
+// CHECK: Actual:   11111111111
+
+// CHECK: *** Test casts from int8 ***
+// CHECK: Expected: 111111111
+// CHECK: Actual:   111111111
+
+// CHECK: *** Test casts from int16 ***
+// CHECK: Expected: 111111111
+// CHECK: Actual:   111111111
+
+// CHECK: *** Test casts from int32 ***
+// CHECK: Expected: 111111111
+// CHECK: Actual:   111111111
+
+// CHECK: *** Test casts from int64 ***
+// CHECK: Expected: 111111111
+// CHECK: Actual:   111111111
+
+// CHECK: *** Test consecutive casts ***
+// CHECK: Expected: 1111
+// CHECK: Actual:   1111
+
+// CHECK: *** Test unsigned truncation ***
+// CHECK: Expected: 1
+// CHECK: Actual:   1
+
+// CHECK: *** Test unsigned extension ***
+// CHECK: Expected: 1
+// CHECK: Actual:   1
+
+// CHECK: *** Test signed to unsigned truncation ***
+// CHECK: Expected: 1
+// CHECK: Actual:   1
+
+// CHECK: *** Test signed to unsigned extension ***
+// CHECK: Expected: 1
+// CHECK: Actual:   1
+
+// CHECK: *** Test unsigned to signed truncation ***
+// CHECK: Expected: 1
+// CHECK: Actual:   1
+
+// CHECK: *** Test unsigned to signed  extension ***
+// CHECK: Expected: 1
+// CHECK: Actual:   1
diff --git a/test/AST-Quake/cast.cpp b/test/AST-Quake/cast.cpp
index 3bef0fb4c87..e905e6b8d0e 100644
--- a/test/AST-Quake/cast.cpp
+++ b/test/AST-Quake/cast.cpp
@@ -13,7 +13,7 @@
 struct testCast {
   void operator()() __qpu__ {
     cudaq::qubit q0, q1;
-    h(q0);    
+    h(q0);
     double bit = mz(q0);
     // This tests implicit casting from double to bool
     if (bit)
@@ -22,6 +22,7 @@ struct testCast {
   }
 };
 
+// clang-format off
 // CHECK-LABEL:   func.func @__nvqpp__mlirgen__testCast() attributes {"cudaq-entrypoint", "cudaq-kernel"} {
 // CHECK:           %[[VAL_0:.*]] = arith.constant 0.000000e+00 : f64
 // CHECK:           %[[VAL_1:.*]] = quake.alloca !quake.ref
@@ -40,3 +41,153 @@ struct testCast {
 // CHECK:           %[[VAL_8:.*]] = quake.mz %[[VAL_2]] : (!quake.ref) -> !quake.measure
 // CHECK:           return
 // CHECK:         }
+// clang-format on
+
+struct testCastBoolMeasurement {
+  void operator()() __qpu__ {
+    cudaq::qubit q0, q1;
+    h(q0);
+    bool bit = mz(q0);
+    // This tests casting from bool to uint32
+    unsigned int i = (unsigned)(bit);
+    if (i == 1)
+      x(q1);
+    mz(q1);
+  }
+};
+
+// clang-format off
+// CHECK-LABEL:   func.func @__nvqpp__mlirgen__testCastBoolMeasurement() attributes {"cudaq-entrypoint", "cudaq-kernel"} {
+// CHECK:           %[[VAL_1_i32:.*]] = arith.constant 1 : i32
+// CHECK:           %[[VAL_0:.*]] = quake.alloca !quake.ref
+// CHECK:           %[[VAL_1:.*]] = quake.alloca !quake.ref
+// CHECK:           quake.h %[[VAL_0]] : (!quake.ref) -> ()
+// CHECK:           %[[VAL_m0:.*]] = quake.mz %[[VAL_0]] name {{.*}} : (!quake.ref) -> !quake.measure
+// CHECK:           %[[VAL_2:.*]] = quake.discriminate %[[VAL_m0]] :
+// CHECK:           %[[VAL_3:.*]] = cc.alloca i1
+// CHECK:           cc.store %[[VAL_2]], %[[VAL_3]] : !cc.ptr<i1>
+// CHECK:           %[[VAL_4:.*]] = cc.load %[[VAL_3]] : !cc.ptr<i1>
+// CHECK:           %[[VAL_5:.*]] = cc.cast unsigned %[[VAL_4]] : (i1) -> i32
+// CHECK:           %[[VAL_6:.*]] = cc.alloca i32
+// CHECK:           cc.store %[[VAL_5]], %[[VAL_6]] : !cc.ptr<i32>
+// CHECK:           %[[VAL_7:.*]] = cc.load %[[VAL_6]] : !cc.ptr<i32>
+// CHECK:           %[[VAL_8:.*]] = arith.cmpi eq, %[[VAL_7:.*]], %[[VAL_1_i32]] : i32
+// CHECK:           cc.if(%[[VAL_8]]) {
+// CHECK:             quake.x %[[VAL_1]] : (!quake.ref) -> ()
+// CHECK:           }
+// CHECK:           %[[VAL_m1:.*]] = quake.mz %[[VAL_1]] : (!quake.ref) -> !quake.measure
+// CHECK:           return
+// CHECK:         }
+// clang-format on
+
+struct testUnsignedCastBoolConstTrue {
+  void operator()() __qpu__ {
+    cudaq::qubit q0;
+    // This tests casting from bool to uint32
+    // and constant folding
+    unsigned i = (unsigned)(true);
+    if (i == 1)
+      x(q0);
+    mz(q0);
+  }
+};
+
+// clang-format off
+// CHECK-LABEL:   func.func @__nvqpp__mlirgen__testUnsignedCastBoolConstTrue() attributes {"cudaq-entrypoint", "cudaq-kernel"} {
+// CHECK:           %[[VAL_1_i32:.*]] = arith.constant 1 : i32
+// CHECK:           %[[VAL_0:.*]] = quake.alloca !quake.ref
+// CHECK:           %[[VAL_1:.*]] = cc.alloca i32
+// CHECK:           cc.store %[[VAL_1_i32]], %[[VAL_1]] : !cc.ptr<i32>
+// CHECK:           %[[VAL_2:.*]] = cc.load %[[VAL_1]] : !cc.ptr<i32>
+// CHECK:           %[[VAL_3:.*]] = arith.cmpi eq, %[[VAL_2:.*]], %[[VAL_1_i32]] : i32
+// CHECK:           cc.if(%[[VAL_3]]) {
+// CHECK:             quake.x %[[VAL_0]] : (!quake.ref) -> ()
+// CHECK:           }
+// CHECK:           %[[VAL_m:.*]] = quake.mz %[[VAL_0]] : (!quake.ref) -> !quake.measure
+// CHECK:           return
+// CHECK:         }
+// clang-format on
+
+struct testUnsignedCastBoolConstFalse {
+  void operator()() __qpu__ {
+    cudaq::qubit q0;
+    // This tests casting from bool to uint32
+    // and constant folding
+    unsigned i = (unsigned)(false);
+    if (i == 0)
+      x(q0);
+    mz(q0);
+  }
+};
+
+// clang-format off
+// CHECK-LABEL:   func.func @__nvqpp__mlirgen__testUnsignedCastBoolConstFalse() attributes {"cudaq-entrypoint", "cudaq-kernel"} {
+// CHECK:           %[[VAL_0_i32:.*]] = arith.constant 0 : i32
+// CHECK:           %[[VAL_0:.*]] = quake.alloca !quake.ref
+// CHECK:           %[[VAL_1:.*]] = cc.alloca i32
+// CHECK:           cc.store %[[VAL_0_i32]], %[[VAL_1]] : !cc.ptr<i32>
+// CHECK:           %[[VAL_2:.*]] = cc.load %[[VAL_1]] : !cc.ptr<i32>
+// CHECK:           %[[VAL_3:.*]] = arith.cmpi eq, %[[VAL_2:.*]], %[[VAL_0_i32]] : i32
+// CHECK:           cc.if(%[[VAL_3]]) {
+// CHECK:             quake.x %[[VAL_0]] : (!quake.ref) -> ()
+// CHECK:           }
+// CHECK:           %[[VAL_m:.*]] = quake.mz %[[VAL_0]] : (!quake.ref) -> !quake.measure
+// CHECK:           return
+// CHECK:         }
+// clang-format on
+
+struct testSignedCastBoolConstTrue {
+  void operator()() __qpu__ {
+    cudaq::qubit q0;
+    // This tests casting from bool to int32
+    // and constant folding
+    signed int i = (signed)(true);
+    if (i == 1)
+      x(q0);
+    mz(q0);
+  }
+};
+
+// clang-format off
+// CHECK-LABEL:   func.func @__nvqpp__mlirgen__testSignedCastBoolConstTrue() attributes {"cudaq-entrypoint", "cudaq-kernel"} {
+// CHECK:           %[[VAL_1_i32:.*]] = arith.constant 1 : i32
+// CHECK:           %[[VAL_0:.*]] = quake.alloca !quake.ref
+// CHECK:           %[[VAL_1:.*]] = cc.alloca i32
+// CHECK:           cc.store %[[VAL_1_i32]], %[[VAL_1]] : !cc.ptr<i32>
+// CHECK:           %[[VAL_2:.*]] = cc.load %[[VAL_1]] : !cc.ptr<i32>
+// CHECK:           %[[VAL_3:.*]] = arith.cmpi eq, %[[VAL_2:.*]], %[[VAL_1_i32]] : i32
+// CHECK:           cc.if(%[[VAL_3]]) {
+// CHECK:             quake.x %[[VAL_0]] : (!quake.ref) -> ()
+// CHECK:           }
+// CHECK:           %[[VAL_m:.*]] = quake.mz %[[VAL_0]] : (!quake.ref) -> !quake.measure
+// CHECK:           return
+// CHECK:         }
+// clang-format on
+
+struct testSignedCastBoolConstFalse {
+  void operator()() __qpu__ {
+    cudaq::qubit q0;
+    // This tests casting from bool to int32
+    // and constant folding.
+    signed int i = (signed)(false);
+    if (i == 0)
+      x(q0);
+    mz(q0);
+  }
+};
+
+// clang-format off
+// CHECK-LABEL:   func.func @__nvqpp__mlirgen__testSignedCastBoolConstFalse() attributes {"cudaq-entrypoint", "cudaq-kernel"} {
+// CHECK:           %[[VAL_0_i32:.*]] = arith.constant 0 : i32
+// CHECK:           %[[VAL_0:.*]] = quake.alloca !quake.ref
+// CHECK:           %[[VAL_1:.*]] = cc.alloca i32
+// CHECK:           cc.store %[[VAL_0_i32]], %[[VAL_1]] : !cc.ptr<i32>
+// CHECK:           %[[VAL_2:.*]] = cc.load %[[VAL_1]] : !cc.ptr<i32>
+// CHECK:           %[[VAL_3:.*]] = arith.cmpi eq, %[[VAL_2:.*]], %[[VAL_0_i32]] : i32
+// CHECK:           cc.if(%[[VAL_3]]) {
+// CHECK:             quake.x %[[VAL_0]] : (!quake.ref) -> ()
+// CHECK:           }
+// CHECK:           %[[VAL_m:.*]] = quake.mz %[[VAL_0]] : (!quake.ref) -> !quake.measure
+// CHECK:           return
+// CHECK:         }
+// clang-format on
diff --git a/test/AST-Quake/loop_normal.cpp b/test/AST-Quake/loop_normal.cpp
index 62ff0aa75b3..e2e76c7443f 100644
--- a/test/AST-Quake/loop_normal.cpp
+++ b/test/AST-Quake/loop_normal.cpp
@@ -632,10 +632,10 @@ __qpu__ int f1b() {
 }
 
 // CHECK-LABEL:   func.func @__nvqpp__mlirgen__function_f1b._Z3f1bv() -> i32 attributes {"cudaq-entrypoint", "cudaq-kernel", no_this} {
-// CHECK:           %[[VAL_0:.*]] = arith.constant false
-// CHECK:           %[[VAL_1:.*]] = arith.constant 1 : i32
-// CHECK:           %[[VAL_2:.*]] = arith.constant 0 : i32
-// CHECK:           %[[VAL_3:.*]] = arith.constant 10 : i32
+// CHECK-DAG:       %[[VAL_0:.*]] = arith.constant false
+// CHECK-DAG:       %[[VAL_1:.*]] = arith.constant 1 : i32
+// CHECK-DAG:       %[[VAL_2:.*]] = arith.constant 0 : i32
+// CHECK-DAG:       %[[VAL_3:.*]] = arith.constant 10 : i32
 // CHECK:           %[[VAL_4:.*]] = quake.alloca !quake.ref
 // CHECK:           %[[VAL_5:.*]] = cc.loop while ((%[[VAL_6:.*]] = %[[VAL_3]]) -> (i32)) {
 // CHECK:             cc.condition %[[VAL_0]](%[[VAL_6]] : i32)
diff --git a/test/Quake/cast_fold.qke b/test/Quake/cast_fold.qke
index 64a7c546e74..58acb4816e4 100644
--- a/test/Quake/cast_fold.qke
+++ b/test/Quake/cast_fold.qke
@@ -93,7 +93,7 @@ func.func @b9() -> i32 {
 // CHECK:         }
 
 // CHECK-LABEL:   func.func @b7() -> i64 {
-// CHECK:           %[[VAL_0:.*]] = arith.constant -82 : i64
+// CHECK:           %[[VAL_0:.*]] = arith.constant 65454 : i64
 // CHECK:           return %[[VAL_0]] : i64
 // CHECK:         }
 
@@ -106,3 +106,337 @@ func.func @b9() -> i32 {
 // CHECK:           %[[VAL_0:.*]] = arith.constant 7 : i32
 // CHECK:           return %[[VAL_0]] : i32
 // CHECK:         }
+
+
+// Cast of bool
+
+func.func @b_ib() -> i1 {
+  %0 = arith.constant true
+  %1 = cc.cast signed %0 : (i1) -> i1
+  return %1 : i1
+}
+
+// CHECK:   func.func @b_ib() -> i1 {
+// CHECK:     %[[VAL_0:.*]] = arith.constant true
+// CHECK:     return %[[VAL_0]] : i1
+// CHECK:   }
+
+func.func @b_ub() -> i1 {
+  %0 = arith.constant true
+  %1 = cc.cast unsigned %0 : (i1) -> i1
+  return %1 : i1
+}
+
+// CHECK:   func.func @b_ub() -> i1 {
+// CHECK:     %[[VAL_0:.*]] = arith.constant true
+// CHECK:     return %[[VAL_0]] : i1
+// CHECK:   }
+
+func.func @b_i8() -> i8 {
+  %0 = arith.constant true
+  %1 = cc.cast signed %0 : (i1) -> i8
+  return %1 : i8
+}
+
+// CHECK:   func.func @b_i8() -> i8 {
+// CHECK:     %[[VAL_0:.*]] = arith.constant -1 : i8
+// CHECK:     return %[[VAL_0]] : i8
+// CHECK:   }
+
+
+func.func @b_u8() -> i8 {
+  %0 = arith.constant true
+  %1 = cc.cast unsigned %0 : (i1) -> i8
+  return %1 : i8
+}
+
+// CHECK:   func.func @b_u8() -> i8 {
+// CHECK:     %[[VAL_0:.*]] = arith.constant 1 : i8
+// CHECK:     return %[[VAL_0]] : i8
+// CHECK:   }
+
+func.func @b_i16() -> i16 {
+  %0 = arith.constant true
+  %1 = cc.cast signed %0 : (i1) -> i16
+  return %1 : i16
+}
+
+// CHECK:   func.func @b_i16() -> i16 {
+// CHECK:     %[[VAL_0:.*]] = arith.constant -1 : i16
+// CHECK:     return %[[VAL_0]] : i16
+// CHECK:   }
+
+func.func @b_u16() -> i16 {
+  %0 = arith.constant true
+  %1 = cc.cast unsigned %0 : (i1) -> i16
+  return %1 : i16
+}
+
+// CHECK:   func.func @b_u16() -> i16 {
+// CHECK:     %[[VAL_0:.*]] = arith.constant 1 : i16
+// CHECK:     return %[[VAL_0]] : i16
+// CHECK:   }
+
+
+func.func @b_i32() -> i32 {
+  %0 = arith.constant true
+  %1 = cc.cast signed %0 : (i1) -> i32
+  return %1 : i32
+}
+
+// CHECK:   func.func @b_i32() -> i32 {
+// CHECK:     %[[VAL_0:.*]] = arith.constant -1 : i32
+// CHECK:     return %[[VAL_0]] : i32
+// CHECK:   }
+
+func.func @b_u32() -> i32 {
+  %0 = arith.constant true
+  %1 = cc.cast unsigned %0 : (i1) -> i32
+  return %1 : i32
+}
+
+// CHECK:   func.func @b_u32() -> i32 {
+// CHECK:     %[[VAL_0:.*]] = arith.constant 1 : i32
+// CHECK:     return %[[VAL_0]] : i32
+// CHECK:   }
+
+func.func @b_i64() -> i64 {
+  %0 = arith.constant true
+  %1 = cc.cast signed %0 : (i1) -> i64
+  return %1 : i64
+}
+
+// CHECK:   func.func @b_i64() -> i64 {
+// CHECK:     %[[VAL_0:.*]] = arith.constant -1 : i64
+// CHECK:     return %[[VAL_0]] : i64
+// CHECK:   }
+
+func.func @b_u64() -> i64 {
+  %0 = arith.constant true
+  %1 = cc.cast unsigned %0 : (i1) -> i64
+  return %1 : i64
+}
+
+// CHECK:   func.func @b_u64() -> i64 {
+// CHECK:     %[[VAL_0:.*]] = arith.constant 1 : i64
+// CHECK:     return %[[VAL_0]] : i64
+// CHECK:   }
+
+
+// Cast of int32
+
+func.func @i32_ib() -> i1 {
+  %0 = arith.constant -1 : i32
+  %1 = cc.cast %0 : (i32) -> i1
+  return %1 : i1
+}
+
+// CHECK:   func.func @i32_ib() -> i1 {
+// CHECK:     %[[VAL_0:.*]] = arith.constant true
+// CHECK:     return %[[VAL_0]] : i1
+// CHECK:   }
+
+func.func @i32_i8() -> i8 {
+  %0 = arith.constant -1 : i32
+  %1 = cc.cast %0 : (i32) -> i8
+  return %1 : i8
+}
+
+// CHECK:   func.func @i32_i8() -> i8 {
+// CHECK:     %[[VAL_0:.*]] = arith.constant -1 : i8
+// CHECK:     return %[[VAL_0]] : i8
+// CHECK:   }
+
+func.func @i32_i16() -> i16 {
+  %0 = arith.constant -1 : i32
+  %1 = cc.cast %0 : (i32) -> i16
+  return %1 : i16
+}
+
+// CHECK:   func.func @i32_i16() -> i16 {
+// CHECK:     %[[VAL_0:.*]] = arith.constant -1 : i16
+// CHECK:     return %[[VAL_0]] : i16
+// CHECK:   }
+
+func.func @i32_i32() -> i32 {
+  %0 = arith.constant -1 : i32
+  %1 = cc.cast signed %0 : (i32) -> i32
+  return %1 : i32
+}
+
+// CHECK:   func.func @i32_i32() -> i32 {
+// CHECK:     %[[VAL_0:.*]] = arith.constant -1 : i32
+// CHECK:     return %[[VAL_0]] : i32
+// CHECK:   }
+
+func.func @i32_u32() -> i32 {
+  %0 = arith.constant -1 : i32
+  %1 = cc.cast unsigned %0 : (i32) -> i32
+  return %1 : i32
+}
+
+// CHECK:   func.func @i32_u32() -> i32 {
+// CHECK:     %[[VAL_0:.*]] = arith.constant -1 : i32
+// CHECK:     return %[[VAL_0]] : i32
+// CHECK:   }
+
+func.func @i32_i64() -> i64 {
+  %0 = arith.constant -1 : i32
+  %1 = cc.cast signed %0 : (i32) -> i64
+  return %1 : i64
+}
+
+// CHECK:   func.func @i32_i64() -> i64 {
+// CHECK:     %[[VAL_0:.*]] = arith.constant -1 : i64
+// CHECK:     return %[[VAL_0]] : i64
+// CHECK:   }
+
+func.func @i32_u64() -> i64 {
+  %0 = arith.constant -1 : i32
+  %1 = cc.cast unsigned %0 : (i32) -> i64
+  return %1 : i64
+}
+
+// CHECK:   func.func @i32_u64() -> i64 {
+// CHECK:     %[[VAL_0:.*]] = arith.constant 4294967295 : i64
+// CHECK:     return %[[VAL_0]] : i64
+// CHECK:   }
+
+
+// Cast of uint32
+
+func.func @u32_u64() -> i64 {
+  %0 = arith.constant 4294967295 : i32
+  %1 = cc.cast unsigned %0 : (i32) -> i64
+  return %1 : i64
+}
+
+// CHECK:   func.func @u32_u64() -> i64 {
+// CHECK:     %[[VAL_0:.*]] = arith.constant 4294967295 : i64
+// CHECK:     return %[[VAL_0]] : i64
+// CHECK:   }
+
+func.func @u32_i64() -> i64 {
+  %0 = arith.constant 4294967295 : i32
+  %1 = cc.cast signed %0 : (i32) -> i64
+  return %1 : i64
+}
+
+// CHECK:   func.func @u32_i64() -> i64 {
+// CHECK:     %[[VAL_0:.*]] = arith.constant -1 : i64
+// CHECK:     return %[[VAL_0]] : i64
+// CHECK:   }
+
+
+// Cast folding optimizations on integer casts
+
+func.func @i32_nop() -> i32 {
+  %0 = arith.constant -1: i32
+  %1 = cc.cast %0 : (i32) -> i32
+  return %1 : i32
+}
+
+// CHECK:   func.func @i32_nop() -> i32 {
+// CHECK:     %[[VAL_0:.*]] = arith.constant -1 : i32
+// CHECK:     return %[[VAL_0]] : i32
+// CHECK:   }
+
+func.func @i32_repeat() -> i32 {
+  %0 = arith.constant -1: i32
+  %1 = cc.cast unsigned %0 : (i32) -> i32
+  %2 = cc.cast unsigned %1 : (i32) -> i32
+  return %2 : i32
+}
+
+// CHECK:   func.func @i32_repeat() -> i32 {
+// CHECK:     %[[VAL_0:.*]] = arith.constant -1 : i32
+// CHECK:     return %[[VAL_0]] : i32
+// CHECK:   }
+
+func.func @i32_change_sign() -> i32 {
+  %0 = arith.constant -1: i32
+  %1 = cc.cast signed %0 : (i32) -> i32
+  %2 = cc.cast unsigned %1 : (i32) -> i32
+  return %2 : i32
+}
+
+// CHECK:   func.func @i32_change_sign() -> i32 {
+// CHECK:     %[[VAL_0:.*]] = arith.constant -1 : i32
+// CHECK:     return %[[VAL_0]] : i32
+// CHECK:   }
+
+func.func @i32_change_type() -> i64 {
+  %0 = arith.constant -1: i32
+  %1 = cc.cast unsigned %0 : (i32) -> i32
+  %2 = cc.cast unsigned %1 : (i32) -> i64
+  return %2 : i64
+}
+
+// CHECK:   func.func @i32_change_type() -> i64 {
+// CHECK:     %[[VAL_0:.*]] = arith.constant 4294967295 : i64
+// CHECK:     return %[[VAL_0]] : i64
+// CHECK:   }
+
+func.func @i32_change_type_and_sign() -> i64 {
+  %0 = arith.constant -1: i32
+  %1 = cc.cast unsigned %0 : (i32) -> i32
+  %2 = cc.cast signed %1 : (i32) -> i64
+  return %2 : i64
+}
+
+// CHECK:   func.func @i32_change_type_and_sign() -> i64 {
+// CHECK:     %[[VAL_0:.*]] = arith.constant -1 : i64
+// CHECK:     return %[[VAL_0]] : i64
+// CHECK:   }
+
+
+// cast pointers
+
+func.func @p32_u64() -> i32 {
+  %0 = cc.alloca i32
+  %1 = cc.cast %0 : (!cc.ptr<i32>) -> i64
+  %2 = cc.cast %1 : (i64) -> i32
+  return %2 : i32
+}
+
+// CHECK:   func.func @p32_u64() -> i32 {
+// CHECK:     %[[VAL_0:.*]] = cc.alloca i32
+// CHECK:     %[[VAL_1:.*]] = cc.cast %[[VAL_0]] : (!cc.ptr<i32>) -> i64
+// CHECK:     %[[VAL_2:.*]] = cc.cast %[[VAL_1]] : (i64) -> i32
+// CHECK:     return %[[VAL_2]] : i32
+// CHECK:   }
+
+// Cast and test
+
+func.func @castAndTest () {
+    %c0 = arith.constant -1 : i32
+    %c1 = arith.constant 4294967295 : i64
+    %0 = cc.cast unsigned %c0 : (i32) -> i32
+    %1 = cc.cast unsigned %0 : (i32) -> i64
+    %2 = quake.alloca !quake.ref
+    %3 = arith.cmpi eq, %1, %c1 : i64
+    cc.if(%3) {
+      quake.x %2 : (!quake.ref) -> ()
+    }
+    return
+  }
+
+// CHECK:   func.func @castAndTest() {
+// CHECK:     %true = arith.constant true
+// CHECK:     %0 = quake.alloca !quake.ref
+// CHECK:     cc.if(%true) {
+// CHECK:       quake.x %0 : (!quake.ref) -> ()
+// CHECK:     }
+// CHECK:     return
+// CHECK:   }
+
+func.func @castIntToBool() -> i1 {
+  %0 = arith.constant 800 : i16
+  %1 = cc.cast %0 : (i16) -> i1
+  return %1: i1
+}
+
+// CHECK:   func.func @castIntToBool() -> i1 {
+// CHECK:     %true = arith.constant true
+// CHECK:     return %true : i1
+// CHECK:   }


Reviews:
user: login: nvidia-dobri
body: Change looks good, might be useful to know the context of when this is important / what the downstream breakage we saw was.

I'm a bit confused by the statement:
> (unsigned)(true) is currently converted to a -1 constant in MLIR

What does -1 in `unsigned` mean? `U..._MAX`?
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-11T12:35:14Z
user: login: schweitzpgi
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-12T17:21:11Z
user: login: schweitzpgi
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-12T17:26:11Z
user: login: schweitzpgi
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-12T17:30:52Z
user: login: annagrin
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-14T17:13:34Z
user: login: annagrin
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-14T17:15:37Z
user: login: annagrin
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-14T17:17:30Z
user: login: schweitzpgi
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-18T15:01:19Z
user: login: schweitzpgi
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-19T00:01:10Z
user: login: annagrin
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-22T06:09:37Z
user: login: annagrin
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-22T06:09:53Z
user: login: annagrin
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-22T06:10:56Z
user: login: schweitzpgi
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-23T21:40:47Z
user: login: schweitzpgi
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-23T21:46:18Z
user: login: annagrin
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-27T18:53:45Z
user: login: annagrin
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-27T22:29:55Z
user: login: annagrin
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-30T23:25:26Z
user: login: schweitzpgi
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-02T18:02:31Z
user: login: schweitzpgi
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-02T18:26:35Z
user: login: annagrin
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-02T20:43:53Z
user: login: annagrin
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-02T20:53:57Z
user: login: annagrin
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-12T17:25:46Z
user: login: schweitzpgi
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T16:05:45Z
user: login: schweitzpgi
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T16:08:47Z
user: login: schweitzpgi
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T16:12:35Z
user: login: schweitzpgi
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T16:14:42Z
user: login: schweitzpgi
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T16:15:51Z
user: login: schweitzpgi
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T16:19:58Z
user: login: schweitzpgi
body: Thanks for all the tests! 
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-22T17:51:04Z

Review comments:
diff_hunk: @@ -0,0 +1,137 @@
+/*******************************************************************************
+ * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates.                  *
+ * All rights reserved.                                                        *
+ *                                                                             *
+ * This source code and the accompanying materials are made available under    *
+ * the terms of the Apache License 2.0 which accompanies this distribution.    *
+ ******************************************************************************/
+
+// clang-format off
+// RUN: nvq++ %cpp_std --enable-mlir %s -o %t && %t | FileCheck %s
+// RUN: nvq++ %cpp_std --target infleqtion      --emulate %s -o %t && %t | FileCheck %s
+// RUN: nvq++ --target ionq                     --emulate %s -o %t && %t | FileCheck %s
+// 2 different IQM machines for 2 different topologies
+// RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s
+// RUN: nvq++ %cpp_std --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s
+// RUN: nvq++ %cpp_std --target oqc                      --emulate %s -o %t && %t | FileCheck %s
+// RUN: nvq++ %cpp_std --target quantinuum               --emulate %s -o %t && %t | FileCheck %s
+// RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi
+// clang-format on
+
+#include <cudaq.h>
+#include <iostream>
+#include <string>
+#include <vector>
+
+struct testConstBool {
+  auto operator()() __qpu__ {
path: targettests/execution/cast.cpp
user: login: nvidia-dobri
body: Is this consistent with what C++ does? (unsigned)(true) feels to me like one of those use-at-your-own-risk things that are maybe not consistent across compilers.
created_at: 2025-04-11T12:29:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -214,6 +214,12 @@ OpFoldResult cudaq::cc::CastOp::fold(FoldAdaptor adaptor) {
       auto val = attr.getInt();
       if (isa<IntegerType>(ty)) {
         auto width = ty.getIntOrFloatBitWidth();
+        auto srcTy = getValue().getType();
path: lib/Optimizer/Dialect/CC/CCOps.cpp
user: login: schweitzpgi
body: The bug here is that this is not respecting the `signed` and `unsigned` flag on the cast. This is the correct code for truncation only.

What needs to be added here are the cases as below with `getSint()` and `getZint()` selectors. I suspect the `getSint()` case works with the original code as well. For `getZInt()`, we should simply mask off higher order bits. That is,
```c++
if (fromWidth < 64)
  val &= (1 << fromWidth) - 1;
```
created_at: 2025-04-12T17:21:11Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,137 @@
+/*******************************************************************************
+ * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates.                  *
+ * All rights reserved.                                                        *
+ *                                                                             *
+ * This source code and the accompanying materials are made available under    *
+ * the terms of the Apache License 2.0 which accompanies this distribution.    *
+ ******************************************************************************/
+
+// clang-format off
+// RUN: nvq++ %cpp_std --enable-mlir %s -o %t && %t | FileCheck %s
+// RUN: nvq++ %cpp_std --target infleqtion      --emulate %s -o %t && %t | FileCheck %s
path: targettests/execution/cast.cpp
user: login: schweitzpgi
body: Since this change is generic and applies to any input at all (canonicalization is always run), I think we can conserve some CPU resources here and just execute the test once. We have other ways of tests all the various targets, and this is never going to change wrt the target.
We can get rid of run lines from line 11 through 18.
created_at: 2025-04-12T17:26:11Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,137 @@
+/*******************************************************************************
+ * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates.                  *
+ * All rights reserved.                                                        *
+ *                                                                             *
+ * This source code and the accompanying materials are made available under    *
+ * the terms of the Apache License 2.0 which accompanies this distribution.    *
+ ******************************************************************************/
+
+// clang-format off
+// RUN: nvq++ %cpp_std --enable-mlir %s -o %t && %t | FileCheck %s
+// RUN: nvq++ %cpp_std --target infleqtion      --emulate %s -o %t && %t | FileCheck %s
+// RUN: nvq++ --target ionq                     --emulate %s -o %t && %t | FileCheck %s
+// 2 different IQM machines for 2 different topologies
+// RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s
+// RUN: nvq++ %cpp_std --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s
+// RUN: nvq++ %cpp_std --target oqc                      --emulate %s -o %t && %t | FileCheck %s
+// RUN: nvq++ %cpp_std --target quantinuum               --emulate %s -o %t && %t | FileCheck %s
+// RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi
+// clang-format on
+
+#include <cudaq.h>
+#include <iostream>
+#include <string>
+#include <vector>
+
+struct testConstBool {
+  auto operator()() __qpu__ {
path: targettests/execution/cast.cpp
user: login: schweitzpgi
body: The syntax is irrelevant in the sense that a `bool true` is guaranteed to be the integer value `1` whether it is evaluated in a signed or unsigned context. It's like writing
```c++
((unsigned)1)
((unsigned)0)
```
Neither cast above changes any bits. (Although, the expression may be a subexpression of another expression.)
created_at: 2025-04-12T17:30:52Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,137 @@
+/*******************************************************************************
+ * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates.                  *
+ * All rights reserved.                                                        *
+ *                                                                             *
+ * This source code and the accompanying materials are made available under    *
+ * the terms of the Apache License 2.0 which accompanies this distribution.    *
+ ******************************************************************************/
+
+// clang-format off
+// RUN: nvq++ %cpp_std --enable-mlir %s -o %t && %t | FileCheck %s
+// RUN: nvq++ %cpp_std --target infleqtion      --emulate %s -o %t && %t | FileCheck %s
+// RUN: nvq++ --target ionq                     --emulate %s -o %t && %t | FileCheck %s
+// 2 different IQM machines for 2 different topologies
+// RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s
+// RUN: nvq++ %cpp_std --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s
+// RUN: nvq++ %cpp_std --target oqc                      --emulate %s -o %t && %t | FileCheck %s
+// RUN: nvq++ %cpp_std --target quantinuum               --emulate %s -o %t && %t | FileCheck %s
+// RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi
+// clang-format on
+
+#include <cudaq.h>
+#include <iostream>
+#include <string>
+#include <vector>
+
+struct testConstBool {
+  auto operator()() __qpu__ {
path: targettests/execution/cast.cpp
user: login: annagrin
body: @nvidia-dobri the tests includes side-by-side output of kernels and straight c++ code for comparison, they are identical after the fixes (they were not before)
created_at: 2025-04-14T17:13:34Z
author_association: COLLABORATOR
diff_hunk: @@ -214,6 +214,12 @@ OpFoldResult cudaq::cc::CastOp::fold(FoldAdaptor adaptor) {
       auto val = attr.getInt();
       if (isa<IntegerType>(ty)) {
         auto width = ty.getIntOrFloatBitWidth();
+        auto srcTy = getValue().getType();
path: lib/Optimizer/Dialect/CC/CCOps.cpp
user: login: annagrin
body: I used c++ to cast the source value (for all integers of interest, not just booleans) to the original type, to avoid possible bugs and platform differences... let me know if you prefer using bit masking instead.
created_at: 2025-04-14T17:15:37Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,137 @@
+/*******************************************************************************
+ * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates.                  *
+ * All rights reserved.                                                        *
+ *                                                                             *
+ * This source code and the accompanying materials are made available under    *
+ * the terms of the Apache License 2.0 which accompanies this distribution.    *
+ ******************************************************************************/
+
+// clang-format off
+// RUN: nvq++ %cpp_std --enable-mlir %s -o %t && %t | FileCheck %s
+// RUN: nvq++ %cpp_std --target infleqtion      --emulate %s -o %t && %t | FileCheck %s
path: targettests/execution/cast.cpp
user: login: annagrin
body: done!
created_at: 2025-04-14T17:17:30Z
author_association: COLLABORATOR
diff_hunk: @@ -604,18 +604,11 @@ Value QuakeBridgeVisitor::integerCoercion(Location loc,
                                           const clang::QualType &clangTy,
                                           Type dstTy, Value srcVal) {
   auto fromTy = getResultType(srcVal.getType());
-  if (dstTy == fromTy)
-    return srcVal;
-
   assert(fromTy.isa<IntegerType>() && dstTy.isa<IntegerType>());
-  if (fromTy.getIntOrFloatBitWidth() < dstTy.getIntOrFloatBitWidth()) {
-    auto mode = (clangTy->isUnsignedIntegerOrEnumerationType())
-                    ? cudaq::cc::CastOpMode::Unsigned
-                    : cudaq::cc::CastOpMode::Signed;
-    return builder.create<cudaq::cc::CastOp>(loc, dstTy, srcVal, mode);
-  }
-  assert(fromTy.getIntOrFloatBitWidth() > dstTy.getIntOrFloatBitWidth());
-  return builder.create<cudaq::cc::CastOp>(loc, dstTy, srcVal);
+  auto mode = (clangTy->isUnsignedIntegerOrEnumerationType())
path: lib/Frontend/nvqpp/ConvertExpr.cpp
user: login: schweitzpgi
body: The mode should never be used when truncating a value.
created_at: 2025-04-18T15:01:19Z
author_association: COLLABORATOR
diff_hunk: @@ -214,8 +251,50 @@ OpFoldResult cudaq::cc::CastOp::fold(FoldAdaptor adaptor) {
       auto val = attr.getInt();
       if (isa<IntegerType>(ty)) {
         auto width = ty.getIntOrFloatBitWidth();
-        return builder.create<arith::ConstantIntOp>(loc, val, width)
-            .getResult();
+        auto srcTy = getValue().getType();
+        auto srcWidth = srcTy.getIntOrFloatBitWidth();
+
+        // Cast the integer value of the cast op to the original type
+        // to get correct const value.
+        auto createConst = [&]<typename S>(S val) -> mlir::Value {
+          std::int64_t v = static_cast<std::int64_t>(val);
+          return builder.create<arith::ConstantIntOp>(loc, v, width)
+              .getResult();
+        };
+
+        auto castAndCreateConst = [&](std::int64_t val) -> mlir::Value {
+          if (getZint()) {
+            switch (srcWidth) {
+            case 1:
+              return createConst.template operator()<bool>(val);
+            case 8:
+              return createConst.template operator()<std::uint8_t>(val);
+            case 16:
+              return createConst.template operator()<std::uint16_t>(val);
+            case 32:
+              return createConst.template operator()<std::uint32_t>(val);
+            case 64:
+              return createConst.template operator()<std::uint64_t>(val);
+            default:
+              return nullptr;
+            }
+          }
+          switch (srcWidth) {
+          case 1:
+            return createConst.template operator()<bool>(val);
+          case 8:
+            return createConst.template operator()<std::int8_t>(val);
+          case 16:
+            return createConst.template operator()<std::int16_t>(val);
+          case 32:
+            return createConst.template operator()<std::int32_t>(val);
+          case 64:
+            return createConst.template operator()<std::int64_t>(val);
+          default:
+            return nullptr;
+          }
+        };
path: lib/Optimizer/Dialect/CC/CCOps.cpp
user: login: schweitzpgi
body: Actually, the integer types can have any number of bits $n$. So this is a lot of extra code that only works for a small number of possible cases. Since we don't handle integers longer than 64 bits, we must handle $0 < n <= 64$ cases.

I presented the correct solution somewhere along the way. An integer that starts out as 64 bits cannot be extended by definition. An integer of less than 64 bits is apparently _already_ sign-extended when we read out the value. (That should be verified.) An integer of less than 64 bits is _not_ properly zero-extended. Zero extension of any $n$ bit 2's complement value is correctly computed with the following bitmasking operations.

```c++
std::int64_t value = /* the sign-extended value that was from the ConstantIntOp */;
value = value & ((1ull << width) - 1);
```
This is a simple one-liner that is correct for any width from 0 to 63. (And 64 is a nop, so we're perfect with this.)
created_at: 2025-04-19T00:01:09Z
author_association: COLLABORATOR
diff_hunk: @@ -214,8 +251,50 @@ OpFoldResult cudaq::cc::CastOp::fold(FoldAdaptor adaptor) {
       auto val = attr.getInt();
       if (isa<IntegerType>(ty)) {
         auto width = ty.getIntOrFloatBitWidth();
-        return builder.create<arith::ConstantIntOp>(loc, val, width)
-            .getResult();
+        auto srcTy = getValue().getType();
+        auto srcWidth = srcTy.getIntOrFloatBitWidth();
+
+        // Cast the integer value of the cast op to the original type
+        // to get correct const value.
+        auto createConst = [&]<typename S>(S val) -> mlir::Value {
+          std::int64_t v = static_cast<std::int64_t>(val);
+          return builder.create<arith::ConstantIntOp>(loc, v, width)
+              .getResult();
+        };
+
+        auto castAndCreateConst = [&](std::int64_t val) -> mlir::Value {
+          if (getZint()) {
+            switch (srcWidth) {
+            case 1:
+              return createConst.template operator()<bool>(val);
+            case 8:
+              return createConst.template operator()<std::uint8_t>(val);
+            case 16:
+              return createConst.template operator()<std::uint16_t>(val);
+            case 32:
+              return createConst.template operator()<std::uint32_t>(val);
+            case 64:
+              return createConst.template operator()<std::uint64_t>(val);
+            default:
+              return nullptr;
+            }
+          }
+          switch (srcWidth) {
+          case 1:
+            return createConst.template operator()<bool>(val);
+          case 8:
+            return createConst.template operator()<std::int8_t>(val);
+          case 16:
+            return createConst.template operator()<std::int16_t>(val);
+          case 32:
+            return createConst.template operator()<std::int32_t>(val);
+          case 64:
+            return createConst.template operator()<std::int64_t>(val);
+          default:
+            return nullptr;
+          }
+        };
path: lib/Optimizer/Dialect/CC/CCOps.cpp
user: login: annagrin
body: I had to zero-extend the source width first to get the original value, in addition to your suggestion. Appreciate another look (it did change some of my new test outcomes)
created_at: 2025-04-22T06:09:37Z
author_association: COLLABORATOR
diff_hunk: @@ -604,18 +604,11 @@ Value QuakeBridgeVisitor::integerCoercion(Location loc,
                                           const clang::QualType &clangTy,
                                           Type dstTy, Value srcVal) {
   auto fromTy = getResultType(srcVal.getType());
-  if (dstTy == fromTy)
-    return srcVal;
-
   assert(fromTy.isa<IntegerType>() && dstTy.isa<IntegerType>());
-  if (fromTy.getIntOrFloatBitWidth() < dstTy.getIntOrFloatBitWidth()) {
-    auto mode = (clangTy->isUnsignedIntegerOrEnumerationType())
-                    ? cudaq::cc::CastOpMode::Unsigned
-                    : cudaq::cc::CastOpMode::Signed;
-    return builder.create<cudaq::cc::CastOp>(loc, dstTy, srcVal, mode);
-  }
-  assert(fromTy.getIntOrFloatBitWidth() > dstTy.getIntOrFloatBitWidth());
-  return builder.create<cudaq::cc::CastOp>(loc, dstTy, srcVal);
+  auto mode = (clangTy->isUnsignedIntegerOrEnumerationType())
path: lib/Frontend/nvqpp/ConvertExpr.cpp
user: login: annagrin
body: done, thanks!
created_at: 2025-04-22T06:09:53Z
author_association: COLLABORATOR
diff_hunk: @@ -214,6 +214,12 @@ OpFoldResult cudaq::cc::CastOp::fold(FoldAdaptor adaptor) {
       auto val = attr.getInt();
       if (isa<IntegerType>(ty)) {
         auto width = ty.getIntOrFloatBitWidth();
+        auto srcTy = getValue().getType();
path: lib/Optimizer/Dialect/CC/CCOps.cpp
user: login: annagrin
body: see the last comment.
created_at: 2025-04-22T06:10:56Z
author_association: COLLABORATOR
diff_hunk: @@ -214,8 +251,50 @@ OpFoldResult cudaq::cc::CastOp::fold(FoldAdaptor adaptor) {
       auto val = attr.getInt();
       if (isa<IntegerType>(ty)) {
         auto width = ty.getIntOrFloatBitWidth();
-        return builder.create<arith::ConstantIntOp>(loc, val, width)
-            .getResult();
+        auto srcTy = getValue().getType();
+        auto srcWidth = srcTy.getIntOrFloatBitWidth();
+
+        // Cast the integer value of the cast op to the original type
+        // to get correct const value.
+        auto createConst = [&]<typename S>(S val) -> mlir::Value {
+          std::int64_t v = static_cast<std::int64_t>(val);
+          return builder.create<arith::ConstantIntOp>(loc, v, width)
+              .getResult();
+        };
+
+        auto castAndCreateConst = [&](std::int64_t val) -> mlir::Value {
+          if (getZint()) {
+            switch (srcWidth) {
+            case 1:
+              return createConst.template operator()<bool>(val);
+            case 8:
+              return createConst.template operator()<std::uint8_t>(val);
+            case 16:
+              return createConst.template operator()<std::uint16_t>(val);
+            case 32:
+              return createConst.template operator()<std::uint32_t>(val);
+            case 64:
+              return createConst.template operator()<std::uint64_t>(val);
+            default:
+              return nullptr;
+            }
+          }
+          switch (srcWidth) {
+          case 1:
+            return createConst.template operator()<bool>(val);
+          case 8:
+            return createConst.template operator()<std::int8_t>(val);
+          case 16:
+            return createConst.template operator()<std::int16_t>(val);
+          case 32:
+            return createConst.template operator()<std::int32_t>(val);
+          case 64:
+            return createConst.template operator()<std::int64_t>(val);
+          default:
+            return nullptr;
+          }
+        };
path: lib/Optimizer/Dialect/CC/CCOps.cpp
user: login: schweitzpgi
body: I don't understand this comment. If we have a 6 bit integer, the lower order 6 bits must be well-defined. That is when we read out the int64_t, the bits `b` must be correct. The bits `?` might be all 0, all 1, or random 0s and 1s.
```
??....??bbbbbb
```
The bit masking arithmetic above, `(1ull << width) - 1`, creates an unsigned 64 bit mask of
```
00...000111111
```
So when that is ANDed with the original value,, we will necessarily have
```
00...000bbbbbb
```
which is the zero-extended value of the i6 value, as expected. There is no need to further zero-extend either value.
created_at: 2025-04-23T21:40:47Z
author_association: COLLABORATOR
diff_hunk: @@ -214,8 +251,50 @@ OpFoldResult cudaq::cc::CastOp::fold(FoldAdaptor adaptor) {
       auto val = attr.getInt();
       if (isa<IntegerType>(ty)) {
         auto width = ty.getIntOrFloatBitWidth();
-        return builder.create<arith::ConstantIntOp>(loc, val, width)
-            .getResult();
+        auto srcTy = getValue().getType();
+        auto srcWidth = srcTy.getIntOrFloatBitWidth();
+
+        // Cast the integer value of the cast op to the original type
+        // to get correct const value.
+        auto createConst = [&]<typename S>(S val) -> mlir::Value {
+          std::int64_t v = static_cast<std::int64_t>(val);
+          return builder.create<arith::ConstantIntOp>(loc, v, width)
+              .getResult();
+        };
+
+        auto castAndCreateConst = [&](std::int64_t val) -> mlir::Value {
+          if (getZint()) {
+            switch (srcWidth) {
+            case 1:
+              return createConst.template operator()<bool>(val);
+            case 8:
+              return createConst.template operator()<std::uint8_t>(val);
+            case 16:
+              return createConst.template operator()<std::uint16_t>(val);
+            case 32:
+              return createConst.template operator()<std::uint32_t>(val);
+            case 64:
+              return createConst.template operator()<std::uint64_t>(val);
+            default:
+              return nullptr;
+            }
+          }
+          switch (srcWidth) {
+          case 1:
+            return createConst.template operator()<bool>(val);
+          case 8:
+            return createConst.template operator()<std::int8_t>(val);
+          case 16:
+            return createConst.template operator()<std::int16_t>(val);
+          case 32:
+            return createConst.template operator()<std::int32_t>(val);
+          case 64:
+            return createConst.template operator()<std::int64_t>(val);
+          default:
+            return nullptr;
+          }
+        };
path: lib/Optimizer/Dialect/CC/CCOps.cpp
user: login: schweitzpgi
body: Sign-extension (if not already performed) can be done with a left-shift followed by a right-shift on a signed int64_t.
created_at: 2025-04-23T21:46:18Z
author_association: COLLABORATOR
diff_hunk: @@ -214,8 +251,50 @@ OpFoldResult cudaq::cc::CastOp::fold(FoldAdaptor adaptor) {
       auto val = attr.getInt();
       if (isa<IntegerType>(ty)) {
         auto width = ty.getIntOrFloatBitWidth();
-        return builder.create<arith::ConstantIntOp>(loc, val, width)
-            .getResult();
+        auto srcTy = getValue().getType();
+        auto srcWidth = srcTy.getIntOrFloatBitWidth();
+
+        // Cast the integer value of the cast op to the original type
+        // to get correct const value.
+        auto createConst = [&]<typename S>(S val) -> mlir::Value {
+          std::int64_t v = static_cast<std::int64_t>(val);
+          return builder.create<arith::ConstantIntOp>(loc, v, width)
+              .getResult();
+        };
+
+        auto castAndCreateConst = [&](std::int64_t val) -> mlir::Value {
+          if (getZint()) {
+            switch (srcWidth) {
+            case 1:
+              return createConst.template operator()<bool>(val);
+            case 8:
+              return createConst.template operator()<std::uint8_t>(val);
+            case 16:
+              return createConst.template operator()<std::uint16_t>(val);
+            case 32:
+              return createConst.template operator()<std::uint32_t>(val);
+            case 64:
+              return createConst.template operator()<std::uint64_t>(val);
+            default:
+              return nullptr;
+            }
+          }
+          switch (srcWidth) {
+          case 1:
+            return createConst.template operator()<bool>(val);
+          case 8:
+            return createConst.template operator()<std::int8_t>(val);
+          case 16:
+            return createConst.template operator()<std::int16_t>(val);
+          case 32:
+            return createConst.template operator()<std::int32_t>(val);
+          case 64:
+            return createConst.template operator()<std::int64_t>(val);
+          default:
+            return nullptr;
+          }
+        };
path: lib/Optimizer/Dialect/CC/CCOps.cpp
user: login: annagrin
body: Note: `width` in the code is the width we are casting to, not the original width of the constant, `srcWidth` is the original width.

The constant already has a sign-extended value of `std::int64_t`, for example, for a one-bit max integer (i.e. `True`), the value of the constant is `(std::int64_t)(-1)`. So to convert it to a `std::uint32_t`, it is not enough to perform `value = value & ((1ull << targetWidth) - 1);`, as it will result in `(std::uint32_t)(-1) = 4294967295` and not in `1`, and indeed my new tests failed.

When I added `value = value & ((1ull << srcWidth) - 1);`, all tests started to agree with c++ code (see the `targettests/execution/cast.cpp`.

So it seems that we need a mask with `min(srcWidth, targetWidth)` 1's - please let me know if I am missing something.
created_at: 2025-04-27T18:53:45Z
author_association: COLLABORATOR
diff_hunk: @@ -214,8 +251,50 @@ OpFoldResult cudaq::cc::CastOp::fold(FoldAdaptor adaptor) {
       auto val = attr.getInt();
       if (isa<IntegerType>(ty)) {
         auto width = ty.getIntOrFloatBitWidth();
-        return builder.create<arith::ConstantIntOp>(loc, val, width)
-            .getResult();
+        auto srcTy = getValue().getType();
+        auto srcWidth = srcTy.getIntOrFloatBitWidth();
+
+        // Cast the integer value of the cast op to the original type
+        // to get correct const value.
+        auto createConst = [&]<typename S>(S val) -> mlir::Value {
+          std::int64_t v = static_cast<std::int64_t>(val);
+          return builder.create<arith::ConstantIntOp>(loc, v, width)
+              .getResult();
+        };
+
+        auto castAndCreateConst = [&](std::int64_t val) -> mlir::Value {
+          if (getZint()) {
+            switch (srcWidth) {
+            case 1:
+              return createConst.template operator()<bool>(val);
+            case 8:
+              return createConst.template operator()<std::uint8_t>(val);
+            case 16:
+              return createConst.template operator()<std::uint16_t>(val);
+            case 32:
+              return createConst.template operator()<std::uint32_t>(val);
+            case 64:
+              return createConst.template operator()<std::uint64_t>(val);
+            default:
+              return nullptr;
+            }
+          }
+          switch (srcWidth) {
+          case 1:
+            return createConst.template operator()<bool>(val);
+          case 8:
+            return createConst.template operator()<std::int8_t>(val);
+          case 16:
+            return createConst.template operator()<std::int16_t>(val);
+          case 32:
+            return createConst.template operator()<std::int32_t>(val);
+          case 64:
+            return createConst.template operator()<std::int64_t>(val);
+          default:
+            return nullptr;
+          }
+        };
path: lib/Optimizer/Dialect/CC/CCOps.cpp
user: login: annagrin
body: Actually, it might be enough to just zero-extend from the source size... I will let it run the tests.
created_at: 2025-04-27T22:29:55Z
author_association: COLLABORATOR
diff_hunk: @@ -214,8 +251,50 @@ OpFoldResult cudaq::cc::CastOp::fold(FoldAdaptor adaptor) {
       auto val = attr.getInt();
       if (isa<IntegerType>(ty)) {
         auto width = ty.getIntOrFloatBitWidth();
-        return builder.create<arith::ConstantIntOp>(loc, val, width)
-            .getResult();
+        auto srcTy = getValue().getType();
+        auto srcWidth = srcTy.getIntOrFloatBitWidth();
+
+        // Cast the integer value of the cast op to the original type
+        // to get correct const value.
+        auto createConst = [&]<typename S>(S val) -> mlir::Value {
+          std::int64_t v = static_cast<std::int64_t>(val);
+          return builder.create<arith::ConstantIntOp>(loc, v, width)
+              .getResult();
+        };
+
+        auto castAndCreateConst = [&](std::int64_t val) -> mlir::Value {
+          if (getZint()) {
+            switch (srcWidth) {
+            case 1:
+              return createConst.template operator()<bool>(val);
+            case 8:
+              return createConst.template operator()<std::uint8_t>(val);
+            case 16:
+              return createConst.template operator()<std::uint16_t>(val);
+            case 32:
+              return createConst.template operator()<std::uint32_t>(val);
+            case 64:
+              return createConst.template operator()<std::uint64_t>(val);
+            default:
+              return nullptr;
+            }
+          }
+          switch (srcWidth) {
+          case 1:
+            return createConst.template operator()<bool>(val);
+          case 8:
+            return createConst.template operator()<std::int8_t>(val);
+          case 16:
+            return createConst.template operator()<std::int16_t>(val);
+          case 32:
+            return createConst.template operator()<std::int32_t>(val);
+          case 64:
+            return createConst.template operator()<std::int64_t>(val);
+          default:
+            return nullptr;
+          }
+        };
path: lib/Optimizer/Dialect/CC/CCOps.cpp
user: login: annagrin
body: Looks like it is, thanks!
created_at: 2025-04-30T23:25:26Z
author_association: COLLABORATOR
diff_hunk: @@ -488,7 +488,7 @@ bool QuakeBridgeVisitor::VisitCXXBoolLiteralExpr(clang::CXXBoolLiteralExpr *x) {
   auto intTy =
       builtinTypeToType(cast<clang::BuiltinType>(x->getType().getTypePtr()));
   auto intVal = x->getValue();
-  return pushValue(getConstantInt(builder, loc, intVal ? 1 : 0, intTy));
+  return pushValue(getConstantInt(builder, loc, intVal, intTy));
path: lib/Frontend/nvqpp/ConvertExpr.cpp
user: login: schweitzpgi
body: Why make this change? Any value that is not zero _must_ be converted to 1. As it was it was safe for all possible values.
created_at: 2025-05-02T17:44:42Z
author_association: COLLABORATOR
diff_hunk: @@ -604,11 +604,8 @@ Value QuakeBridgeVisitor::integerCoercion(Location loc,
                                           const clang::QualType &clangTy,
                                           Type dstTy, Value srcVal) {
   auto fromTy = getResultType(srcVal.getType());
-  if (dstTy == fromTy)
path: lib/Frontend/nvqpp/ConvertExpr.cpp
user: login: schweitzpgi
body: Why make this change? If the type is exactly the same, then there is no coercion and nothing should be done.
created_at: 2025-05-02T17:45:35Z
author_association: COLLABORATOR
diff_hunk: @@ -200,9 +200,46 @@ void cudaq::cc::AllocaOp::getCanonicalizationPatterns(
 //===----------------------------------------------------------------------===//
 
 OpFoldResult cudaq::cc::CastOp::fold(FoldAdaptor adaptor) {
-  // If cast is a nop, just forward the argument to the uses.
-  if (getType() == getValue().getType())
-    return getValue();
+  // Replace unnecessary casts.
+  if (getType() == getValue().getType()) {
+    auto val = getValue();
+    auto ty = getType();
+
+    // Cast is a nop, just forward the argument to the uses.
+    if (!isa<IntegerType>(ty))
+      return val;
+
+    // Cast that does not change the type or the sign.
+    if (!getZint() && !getSint())
+      return val;
+
+    // Cast of a boolean to a boolean.
+    if (ty.getIntOrFloatBitWidth() == 1)
+      return val;
path: lib/Optimizer/Dialect/CC/CCOps.cpp
user: login: schweitzpgi
body: Why not fold something like
```mlir
%5 = arith.constant 800 : i16
%6 = cc.cast %5 : (i16) -> i1
```
Note that this example shows why a conditional test is always better. The value 800 is true and we want `val ? 1 : 0` to convert that 800 to 1.
created_at: 2025-05-02T17:55:00Z
author_association: COLLABORATOR
diff_hunk: @@ -200,9 +200,46 @@ void cudaq::cc::AllocaOp::getCanonicalizationPatterns(
 //===----------------------------------------------------------------------===//
 
 OpFoldResult cudaq::cc::CastOp::fold(FoldAdaptor adaptor) {
-  // If cast is a nop, just forward the argument to the uses.
-  if (getType() == getValue().getType())
-    return getValue();
+  // Replace unnecessary casts.
+  if (getType() == getValue().getType()) {
+    auto val = getValue();
+    auto ty = getType();
+
+    // Cast is a nop, just forward the argument to the uses.
+    if (!isa<IntegerType>(ty))
+      return val;
+
+    // Cast that does not change the type or the sign.
+    if (!getZint() && !getSint())
+      return val;
+
+    // Cast of a boolean to a boolean.
+    if (ty.getIntOrFloatBitWidth() == 1)
+      return val;
+
+    // Cast of a known unsigned value to an unsigned value.
+    if (val.getDefiningOp<cc::StdvecSizeOp>() ||
+        val.getDefiningOp<quake::VeqSizeOp>())
+      if (getZint())
+        return val;
+
+    // Fold consecutive casts when possible.
path: lib/Optimizer/Dialect/CC/CCOps.cpp
user: login: schweitzpgi
body: Eliminating cast chains is already done someplace ...
created_at: 2025-05-02T18:01:41Z
author_association: COLLABORATOR
diff_hunk: @@ -200,9 +200,46 @@ void cudaq::cc::AllocaOp::getCanonicalizationPatterns(
 //===----------------------------------------------------------------------===//
 
 OpFoldResult cudaq::cc::CastOp::fold(FoldAdaptor adaptor) {
-  // If cast is a nop, just forward the argument to the uses.
-  if (getType() == getValue().getType())
-    return getValue();
+  // Replace unnecessary casts.
+  if (getType() == getValue().getType()) {
+    auto val = getValue();
+    auto ty = getType();
+
+    // Cast is a nop, just forward the argument to the uses.
+    if (!isa<IntegerType>(ty))
+      return val;
+
+    // Cast that does not change the type or the sign.
+    if (!getZint() && !getSint())
+      return val;
+
+    // Cast of a boolean to a boolean.
+    if (ty.getIntOrFloatBitWidth() == 1)
+      return val;
+
+    // Cast of a known unsigned value to an unsigned value.
+    if (val.getDefiningOp<cc::StdvecSizeOp>() ||
+        val.getDefiningOp<quake::VeqSizeOp>())
+      if (getZint())
+        return val;
+
+    // Fold consecutive casts when possible.
path: lib/Optimizer/Dialect/CC/CCOps.cpp
user: login: schweitzpgi
body: This one belongs in FuseCastCascade.
created_at: 2025-05-02T18:26:35Z
author_association: COLLABORATOR
diff_hunk: @@ -488,7 +488,7 @@ bool QuakeBridgeVisitor::VisitCXXBoolLiteralExpr(clang::CXXBoolLiteralExpr *x) {
   auto intTy =
       builtinTypeToType(cast<clang::BuiltinType>(x->getType().getTypePtr()));
   auto intVal = x->getValue();
-  return pushValue(getConstantInt(builder, loc, intVal ? 1 : 0, intTy));
+  return pushValue(getConstantInt(builder, loc, intVal, intTy));
path: lib/Frontend/nvqpp/ConvertExpr.cpp
user: login: annagrin
body: I made this change when I tried to understand why we always create a `-1` for a boolean constant `true`, and it seemed redundant to do the conversion here at the time. Later I failed to find a test that fails, for example, I thought `cast<bool>(2)`  should fail as we would zero-extend the original value of `2` it to a `0`.  I suspect we never see a `cast<int>(boolLiteral(2))` in the AST. 

The compiler probably just casts it automatically to `true` before we see it... Will revert for safety.
created_at: 2025-05-02T20:43:53Z
author_association: COLLABORATOR
diff_hunk: @@ -604,11 +604,8 @@ Value QuakeBridgeVisitor::integerCoercion(Location loc,
                                           const clang::QualType &clangTy,
                                           Type dstTy, Value srcVal) {
   auto fromTy = getResultType(srcVal.getType());
-  if (dstTy == fromTy)
path: lib/Frontend/nvqpp/ConvertExpr.cpp
user: login: annagrin
body: there can be still a cast needed to unsigned to zero-extend the original value, which is 1-extended to 64bits in the constant. I'll confirm that with the tests
created_at: 2025-05-02T20:45:36Z
author_association: COLLABORATOR
diff_hunk: @@ -200,9 +200,46 @@ void cudaq::cc::AllocaOp::getCanonicalizationPatterns(
 //===----------------------------------------------------------------------===//
 
 OpFoldResult cudaq::cc::CastOp::fold(FoldAdaptor adaptor) {
-  // If cast is a nop, just forward the argument to the uses.
-  if (getType() == getValue().getType())
-    return getValue();
+  // Replace unnecessary casts.
+  if (getType() == getValue().getType()) {
+    auto val = getValue();
+    auto ty = getType();
+
+    // Cast is a nop, just forward the argument to the uses.
+    if (!isa<IntegerType>(ty))
+      return val;
+
+    // Cast that does not change the type or the sign.
+    if (!getZint() && !getSint())
+      return val;
+
+    // Cast of a boolean to a boolean.
+    if (ty.getIntOrFloatBitWidth() == 1)
+      return val;
path: lib/Optimizer/Dialect/CC/CCOps.cpp
user: login: annagrin
body: Yep that was not optimizing correctly. Added a check of 0, and a test. Thanks!
created_at: 2025-05-02T20:50:26Z
author_association: COLLABORATOR
diff_hunk: @@ -200,9 +200,46 @@ void cudaq::cc::AllocaOp::getCanonicalizationPatterns(
 //===----------------------------------------------------------------------===//
 
 OpFoldResult cudaq::cc::CastOp::fold(FoldAdaptor adaptor) {
-  // If cast is a nop, just forward the argument to the uses.
-  if (getType() == getValue().getType())
-    return getValue();
+  // Replace unnecessary casts.
+  if (getType() == getValue().getType()) {
+    auto val = getValue();
+    auto ty = getType();
+
+    // Cast is a nop, just forward the argument to the uses.
+    if (!isa<IntegerType>(ty))
+      return val;
+
+    // Cast that does not change the type or the sign.
+    if (!getZint() && !getSint())
+      return val;
+
+    // Cast of a boolean to a boolean.
+    if (ty.getIntOrFloatBitWidth() == 1)
+      return val;
+
+    // Cast of a known unsigned value to an unsigned value.
+    if (val.getDefiningOp<cc::StdvecSizeOp>() ||
+        val.getDefiningOp<quake::VeqSizeOp>())
+      if (getZint())
+        return val;
+
+    // Fold consecutive casts when possible.
path: lib/Optimizer/Dialect/CC/CCOps.cpp
user: login: annagrin
body: will do, thanks!
created_at: 2025-05-02T20:51:03Z
author_association: COLLABORATOR
diff_hunk: @@ -488,7 +488,7 @@ bool QuakeBridgeVisitor::VisitCXXBoolLiteralExpr(clang::CXXBoolLiteralExpr *x) {
   auto intTy =
       builtinTypeToType(cast<clang::BuiltinType>(x->getType().getTypePtr()));
   auto intVal = x->getValue();
-  return pushValue(getConstantInt(builder, loc, intVal ? 1 : 0, intTy));
+  return pushValue(getConstantInt(builder, loc, intVal, intTy));
path: lib/Frontend/nvqpp/ConvertExpr.cpp
user: login: annagrin
body: done!
created_at: 2025-05-09T23:02:44Z
author_association: COLLABORATOR
diff_hunk: @@ -604,11 +604,8 @@ Value QuakeBridgeVisitor::integerCoercion(Location loc,
                                           const clang::QualType &clangTy,
                                           Type dstTy, Value srcVal) {
   auto fromTy = getResultType(srcVal.getType());
-  if (dstTy == fromTy)
path: lib/Frontend/nvqpp/ConvertExpr.cpp
user: login: annagrin
body: the tests pass if I bring back the check, updated.
created_at: 2025-05-09T23:13:21Z
author_association: COLLABORATOR
diff_hunk: @@ -200,9 +200,46 @@ void cudaq::cc::AllocaOp::getCanonicalizationPatterns(
 //===----------------------------------------------------------------------===//
 
 OpFoldResult cudaq::cc::CastOp::fold(FoldAdaptor adaptor) {
-  // If cast is a nop, just forward the argument to the uses.
-  if (getType() == getValue().getType())
-    return getValue();
+  // Replace unnecessary casts.
+  if (getType() == getValue().getType()) {
+    auto val = getValue();
+    auto ty = getType();
+
+    // Cast is a nop, just forward the argument to the uses.
+    if (!isa<IntegerType>(ty))
+      return val;
+
+    // Cast that does not change the type or the sign.
+    if (!getZint() && !getSint())
+      return val;
+
+    // Cast of a boolean to a boolean.
+    if (ty.getIntOrFloatBitWidth() == 1)
+      return val;
+
+    // Cast of a known unsigned value to an unsigned value.
+    if (val.getDefiningOp<cc::StdvecSizeOp>() ||
+        val.getDefiningOp<quake::VeqSizeOp>())
+      if (getZint())
+        return val;
+
+    // Fold consecutive casts when possible.
path: lib/Optimizer/Dialect/CC/CCOps.cpp
user: login: annagrin
body: done!
created_at: 2025-05-12T17:24:36Z
author_association: COLLABORATOR
diff_hunk: @@ -378,13 +414,14 @@ LogicalResult cudaq::cc::CastOp::verify() {
 
 namespace {
 // There are a number of series of casts that can be fused. For now, fuse
-// pointer cast chains.
+// pointer cast chains and some integer cast chains.
 struct FuseCastCascade : public OpRewritePattern<cudaq::cc::CastOp> {
   using OpRewritePattern::OpRewritePattern;
 
   LogicalResult matchAndRewrite(cudaq::cc::CastOp castOp,
                                 PatternRewriter &rewriter) const override {
-    if (auto castToCast = castOp.getValue().getDefiningOp<cudaq::cc::CastOp>())
+    if (auto castToCast =
+            castOp.getValue().getDefiningOp<cudaq::cc::CastOp>()) {
path: lib/Optimizer/Dialect/CC/CCOps.cpp
user: login: schweitzpgi
body: Why did clang-format change this line?
created_at: 2025-05-22T16:05:45Z
author_association: COLLABORATOR
diff_hunk: @@ -395,6 +432,19 @@ struct FuseCastCascade : public OpRewritePattern<cudaq::cc::CastOp> {
                                                        castToCast.getValue());
         return success();
       }
+      if (isa<IntegerType>(castOp.getType()) &&
+          (castOp.getType() == castToCast.getType())) {
+        // %4 = cc.cast %3 : (i64) -> i64
+        // %5 = cc.cast unsigned %4 : (i64) -> i64
+        // ────────────────────────────────────────────
+        // %5 = cc.cast unsigned %3 : (i64) -> i64
path: lib/Optimizer/Dialect/CC/CCOps.cpp
user: login: schweitzpgi
body: This is an example of a NOP feeding a NOP. All uses of `%4` and `%5` can be replaced with `%3`.
created_at: 2025-05-22T16:08:47Z
author_association: COLLABORATOR
diff_hunk: @@ -332,9 +332,9 @@ struct FreeRangeChicken {
 // CHECK:           %[[VAL_10:.*]] = cc.load %[[VAL_7]] : !cc.ptr<i32>
 // CHECK:           %[[VAL_11:.*]] = cc.load %[[VAL_8]] : !cc.ptr<i32>
 // CHECK:           %[[VAL_12:.*]] = cc.cast signed %[[VAL_9]] : (i32) -> i64
-// CHECK:           %[[VAL_13:.*]] = cc.cast signed %[[VAL_10]] : (i32) -> i64
-// CHECK:           %[[VAL_14:.*]] = cc.cast signed %[[VAL_11]] : (i32) -> i64
-// CHECK:           %[[VAL_16:.*]] = call @__nvqpp_CudaqSizeFromTriple(%[[VAL_12]], %[[VAL_13]], %[[VAL_14]]) : (i64, i64, i64) -> i64
+// CHECK:           %[[VAL_13:.*]] = cc.cast signed %[[VAL_11]] : (i32) -> i64
+// CHECK:           %[[VAL_14:.*]] = cc.cast signed %[[VAL_10]] : (i32) -> i64
path: test/AST-Quake/ranged_for.cpp
user: login: schweitzpgi
body: CHECK-DAG ?
created_at: 2025-05-22T16:12:35Z
author_association: COLLABORATOR
diff_hunk: @@ -26,15 +26,16 @@ __qpu__ int std_reverse_std_vector_int() {
 // CHECK-DAG:       %[[VAL_1:.*]] = arith.constant 1 : i64
 // CHECK-DAG:       %[[VAL_3:.*]] = arith.constant 0 : i64
 // CHECK-DAG:       %[[VAL_4:.*]] = arith.constant 8 : i64
-// CHECK-DAG:       %[[VAL_5:.*]] = cc.alloca !cc.array<i32 x 10>
path: test/AST-Quake/reverse.cpp
user: login: schweitzpgi
body: CHECK-DAG is fine here. The cc.alloca doesn't depend on any arith.constant value and we don't necessarily want to test to depend on the somewhat arbitrary MLIR rule that constants currently always come before other dialect ops.
created_at: 2025-05-22T16:14:42Z
author_association: COLLABORATOR
diff_hunk: @@ -26,15 +26,16 @@ __qpu__ int std_reverse_std_vector_int() {
 // CHECK-DAG:       %[[VAL_1:.*]] = arith.constant 1 : i64
 // CHECK-DAG:       %[[VAL_3:.*]] = arith.constant 0 : i64
 // CHECK-DAG:       %[[VAL_4:.*]] = arith.constant 8 : i64
-// CHECK-DAG:       %[[VAL_5:.*]] = cc.alloca !cc.array<i32 x 10>
-// CHECK:           %[[VAL_6:.*]] = cc.cast %[[VAL_5]] :
+// CHECK:           %[[VAL_5:.*]] = cc.alloca !cc.array<i32 x 10>
+// CHECK:           %[[VAL_6:.*]] = cc.cast %[[VAL_5]] : (!cc.ptr<!cc.array<i32 x 10>>) -> !cc.ptr<i32>
 // CHECK:           %[[VAL_7:.*]] = cc.compute_ptr %[[VAL_5]][10] : (!cc.ptr<!cc.array<i32 x 10>>) -> !cc.ptr<i32>
 // CHECK:           %[[VAL_8:.*]] = cc.cast %[[VAL_7]] : (!cc.ptr<i32>) -> i64
 // CHECK:           %[[VAL_9:.*]] = cc.cast %[[VAL_6]] : (!cc.ptr<i32>) -> i64
 // CHECK:           %[[VAL_10:.*]] = arith.subi %[[VAL_8]], %[[VAL_9]] : i64
 // CHECK:           %[[VAL_11:.*]] = arith.divsi %[[VAL_10]], %[[VAL_4]] : i64
+// CHECK:           %[[VAL_12:.*]] = cc.cast unsigned %[[VAL_11]] : (i64) -> i64
path: test/AST-Quake/reverse.cpp
user: login: schweitzpgi
body: What is the purpose of adding this operation? It has no semantics.
created_at: 2025-05-22T16:15:51Z
author_association: COLLABORATOR
diff_hunk: @@ -178,10 +178,14 @@ func.func @d(%c2: i64, %c3: i64, %c1: i64, %off: i16) {
 // CHECK-LABEL:   func.func @d(
 // CHECK-SAME:      %[[VAL_0:.*]]: i64, %[[VAL_1:.*]]: i64, %[[VAL_2:.*]]: i64, %[[VAL_3:.*]]: i16) {
 // CHECK:           %[[VAL_4:.*]] = quake.alloca !quake.veq<4>
-// CHECK:           %[[VAL_5:.*]] = arith.addi %[[VAL_0]], %[[VAL_2]] : i64
+// CHECK:           %[[VAL_0_casted:.*]] = cc.cast unsigned %[[VAL_0]] : (i64) -> i64
+// CHECK:           %[[VAL_2_casted:.*]] = cc.cast unsigned %[[VAL_2]] : (i64) -> i64
path: test/Quake/combine.qke
user: login: schweitzpgi
body: I don't understand why this is adding these NOPs.

If we have a 64 bit value of 0xDeadBeefCafeBabe and "sign extend" or "zero extend" it to a 64 bit value we will always get the exact same value, 0xDeadBeefCafeBabe. These casts should be removed as dead code.
created_at: 2025-05-22T16:19:58Z
author_association: COLLABORATOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-04-11T00:33:34Z
body: **CUDA Quantum Docs Bot:** A preview of the documentation can be found [here](https://htmlpreview.github.io/?https://github.com/NVIDIA/cuda-quantum/blob/docs/preview/pr-2805/index.html).
author_association: NONE
user: login: annagrin
created_at: 2025-04-14T17:23:24Z
body: > Change looks good, might be useful to know the context of when this is important / what the downstream breakage we saw was.

I included an example in the description and the reference to the bug it fixes

> 
> I'm a bit confused by the statement:
> 
> > (unsigned)(true) is currently converted to a -1 constant in MLIR
> 
> What does -1 in `unsigned` mean? `U..._MAX`?

Constant folding folds the cast, so in this particular example we end up with an integer constant with value of -1, of type i32 (see example in the description). It would behave the same as uint32 max, yes, which is incorrect in this case, we should const fold it to `arith.constant 1` instead.

author_association: COLLABORATOR
user: login: github-actions[bot]
created_at: 2025-04-14T18:42:43Z
body: **CUDA Quantum Docs Bot:** A preview of the documentation can be found [here](https://htmlpreview.github.io/?https://github.com/NVIDIA/cuda-quantum/blob/docs/preview/pr-2805/index.html).
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-14T20:22:06Z
body: **CUDA Quantum Docs Bot:** A preview of the documentation can be found [here](https://htmlpreview.github.io/?https://github.com/NVIDIA/cuda-quantum/blob/docs/preview/pr-2805/index.html).
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-17T20:01:57Z
body: **CUDA Quantum Docs Bot:** A preview of the documentation can be found [here](https://htmlpreview.github.io/?https://github.com/NVIDIA/cuda-quantum/blob/docs/preview/pr-2805/index.html).
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-17T21:23:32Z
body: **CUDA Quantum Docs Bot:** A preview of the documentation can be found [here](https://htmlpreview.github.io/?https://github.com/NVIDIA/cuda-quantum/blob/docs/preview/pr-2805/index.html).
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-21T05:22:47Z
body: **CUDA Quantum Docs Bot:** A preview of the documentation can be found [here](https://htmlpreview.github.io/?https://github.com/NVIDIA/cuda-quantum/blob/docs/preview/pr-2805/index.html).
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-22T07:27:49Z
body: **CUDA Quantum Docs Bot:** A preview of the documentation can be found [here](https://htmlpreview.github.io/?https://github.com/NVIDIA/cuda-quantum/blob/docs/preview/pr-2805/index.html).
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-22T18:08:27Z
body: **CUDA Quantum Docs Bot:** A preview of the documentation can be found [here](https://htmlpreview.github.io/?https://github.com/NVIDIA/cuda-quantum/blob/docs/preview/pr-2805/index.html).
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-27T21:37:03Z
body: **CUDA Quantum Docs Bot:** A preview of the documentation can be found [here](https://htmlpreview.github.io/?https://github.com/NVIDIA/cuda-quantum/blob/docs/preview/pr-2805/index.html).
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-28T01:21:51Z
body: **CUDA Quantum Docs Bot:** A preview of the documentation can be found [here](https://htmlpreview.github.io/?https://github.com/NVIDIA/cuda-quantum/blob/docs/preview/pr-2805/index.html).
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-01T01:03:17Z
body: **CUDA Quantum Docs Bot:** A preview of the documentation can be found [here](https://htmlpreview.github.io/?https://github.com/NVIDIA/cuda-quantum/blob/docs/preview/pr-2805/index.html).
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-01T18:59:38Z
body: **CUDA Quantum Docs Bot:** A preview of the documentation can be found [here](https://htmlpreview.github.io/?https://github.com/NVIDIA/cuda-quantum/blob/docs/preview/pr-2805/index.html).
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-10T01:11:21Z
body: **CUDA Quantum Docs Bot:** A preview of the documentation can be found [here](https://htmlpreview.github.io/?https://github.com/NVIDIA/cuda-quantum/blob/docs/preview/pr-2805/index.html).
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-12T18:58:31Z
body: **CUDA Quantum Docs Bot:** A preview of the documentation can be found [here](https://htmlpreview.github.io/?https://github.com/NVIDIA/cuda-quantum/blob/docs/preview/pr-2805/index.html).
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-13T03:18:48Z
body: **CUDA Quantum Docs Bot:** A preview of the documentation can be found [here](https://htmlpreview.github.io/?https://github.com/NVIDIA/cuda-quantum/blob/docs/preview/pr-2805/index.html).
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-14T07:01:37Z
body: **CUDA Quantum Docs Bot:** A preview of the documentation can be found [here](https://htmlpreview.github.io/?https://github.com/NVIDIA/cuda-quantum/blob/docs/preview/pr-2805/index.html).
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-14T19:10:38Z
body: **CUDA Quantum Docs Bot:** A preview of the documentation can be found [here](https://htmlpreview.github.io/?https://github.com/NVIDIA/cuda-quantum/blob/docs/preview/pr-2805/index.html).
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-15T00:35:33Z
body: **CUDA Quantum Docs Bot:** A preview of the documentation can be found [here](https://htmlpreview.github.io/?https://github.com/NVIDIA/cuda-quantum/blob/docs/preview/pr-2805/index.html).
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-22T17:27:22Z
body: **CUDA Quantum Docs Bot:** A preview of the documentation can be found [here](https://htmlpreview.github.io/?https://github.com/NVIDIA/cuda-quantum/blob/docs/preview/pr-2805/index.html).
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-22T21:26:42Z
body: **CUDA Quantum Docs Bot:** A preview of the documentation can be found [here](https://htmlpreview.github.io/?https://github.com/NVIDIA/cuda-quantum/blob/docs/preview/pr-2805/index.html).
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-23T13:51:42Z
body: **CUDA Quantum Docs Bot:** A preview of the documentation can be found [here](https://htmlpreview.github.io/?https://github.com/NVIDIA/cuda-quantum/blob/docs/preview/pr-2805/index.html).
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-23T15:28:07Z
body: **CUDA Quantum Docs Bot:** A preview of the documentation can be found [here](https://htmlpreview.github.io/?https://github.com/NVIDIA/cuda-quantum/blob/docs/preview/pr-2805/index.html).
author_association: NONE