M
Modular12mo ago
JIMC

Trying to do compile time checking with 'const' type arguments.

struct ExposedSecret[T: AnyRegType]:
var borrowed_value: T

fn __init__(inout self, borrowed borrowed_value: T):
self.borrowed_value = borrowed_value

struct Secret[T: AnyRegType, MEC: Int, EC: Int]:
var inner: T

fn __init__[EC: Int=EC](inout self, owned value: T):
constrained[EC == 0, "`Secret[T, MEC, EC]` can only be constructed iff `EC == 0`"]()
self.inner = value

@staticmethod
fn ec_add_one(owned other: Self) -> Secret[T, MEC, EC + 1]:
return other^

fn expose_secret[ReturnedType: AnyRegType](owned self, owned closure:
fn(exposed_secret: ExposedSecret[T]) capturing -> ReturnedType)
-> ListLiteral[ReturnedType, Secret[T, MEC, EC + 1]]:
constrained[EC < MEC, "`expose_secret` can only be called if `EC < MEC`"]()
let returned_value = closure(ExposedSecret[T])
let new_self = Secret.ec_add_one(self^)
return [returned_value, new_self]

fn main():
let my_secret = Secret[Int, 3, 2](69)

@parameter
fn capturing_closure_but_actually_never_capture(borrowed exposed_secret: ExposedSecret[Int]) -> Int:
return exposed_secret.borrowed_value

let my_69_secret_tuple = my_secret^.expose_secret[Int](capturing_closure_but_actually_never_capture)
let returned_69_secret = my_69_secret_tuple.get[0, Int]()
let my_new_secret = my_69_secret_tuple^.get[1, Secret[Int, 3, 3]]()
struct ExposedSecret[T: AnyRegType]:
var borrowed_value: T

fn __init__(inout self, borrowed borrowed_value: T):
self.borrowed_value = borrowed_value

struct Secret[T: AnyRegType, MEC: Int, EC: Int]:
var inner: T

fn __init__[EC: Int=EC](inout self, owned value: T):
constrained[EC == 0, "`Secret[T, MEC, EC]` can only be constructed iff `EC == 0`"]()
self.inner = value

@staticmethod
fn ec_add_one(owned other: Self) -> Secret[T, MEC, EC + 1]:
return other^

fn expose_secret[ReturnedType: AnyRegType](owned self, owned closure:
fn(exposed_secret: ExposedSecret[T]) capturing -> ReturnedType)
-> ListLiteral[ReturnedType, Secret[T, MEC, EC + 1]]:
constrained[EC < MEC, "`expose_secret` can only be called if `EC < MEC`"]()
let returned_value = closure(ExposedSecret[T])
let new_self = Secret.ec_add_one(self^)
return [returned_value, new_self]

fn main():
let my_secret = Secret[Int, 3, 2](69)

@parameter
fn capturing_closure_but_actually_never_capture(borrowed exposed_secret: ExposedSecret[Int]) -> Int:
return exposed_secret.borrowed_value

let my_69_secret_tuple = my_secret^.expose_secret[Int](capturing_closure_but_actually_never_capture)
let returned_69_secret = my_69_secret_tuple.get[0, Int]()
let my_new_secret = my_69_secret_tuple^.get[1, Secret[Int, 3, 3]]()
The error:
/root/mojo_projs/sosecrets-mojo/src/secret.mojo:42:70: error: invalid call to 'get': result cannot bind AnyRegType type to memory-only type 'Secret[Int, 3, 3]'
let my_new_secret = my_69_secret_tuple^.get[1, Secret[Int, 3, 3]]()
~~~~~~~~~~~~~~~~~~~~~~~~~~~^~
/root/mojo_projs/sosecrets-mojo/src/secret.mojo:1:1: note: function declared here

^
mojo: error: failed to parse the provided Mojo
/root/mojo_projs/sosecrets-mojo/src/secret.mojo:42:70: error: invalid call to 'get': result cannot bind AnyRegType type to memory-only type 'Secret[Int, 3, 3]'
let my_new_secret = my_69_secret_tuple^.get[1, Secret[Int, 3, 3]]()
~~~~~~~~~~~~~~~~~~~~~~~~~~~^~
/root/mojo_projs/sosecrets-mojo/src/secret.mojo:1:1: note: function declared here

^
mojo: error: failed to parse the provided Mojo
6 Replies
JIMC
JIMCOP12mo ago
Trying to create a Secret class in Mojo such that if EC is greater than MEC, i.e. expose_secret() method has been called too many times, then it raises a compile time error. Sadly, I cannot really use Mojo ListLiteral or Tuple for this as I am returing a Tuple with my Secret[T, MEC, EC+1] type therein but I cannot retrieve it. @Moderator
struct ExposedSecret[T: AnyRegType]:
var borrowed_value: T

fn __init__(inout self, borrowed borrowed_value: T):
self.borrowed_value = borrowed_value

struct Secret[T: AnyRegType, MEC: Int, EC: Int]:
var inner: T

fn __init__[EC: Int=EC](inout self, owned value: T):
constrained[EC == 0, "`Secret[T, MEC, EC]` can only be constructed iff `EC == 0`"]()
self.inner = value

@staticmethod
fn ec_add_one(owned other: Self) -> Secret[T, MEC, EC + 1]:
return other^

fn expose_secret[ReturnedType: AnyRegType](owned self, owned closure:
fn(exposed_secret: ExposedSecret[T]) capturing -> ReturnedType)
-> ListLiteral[ReturnedType, Secret[T, MEC, EC + 1]]:
constrained[EC < MEC, "`expose_secret` can only be called if `EC < MEC`"]()
let returned_value = closure(ExposedSecret[T](self.inner))
let new_self = Secret.ec_add_one(self^)
return [returned_value, new_self^]

fn main():
alias my_secret = Secret[Int, 3, 0](69)

@parameter
fn capturing_closure_but_actually_never_capture(borrowed exposed_secret: ExposedSecret[Int]) -> Int:
return exposed_secret.borrowed_value

let my_69_secret_tuple = my_secret.expose_secret[Int](capturing_closure_but_actually_never_capture)
let returned_69_secret = my_69_secret_tuple.get[0, Int]()
let my_new_secret = my_69_secret_tuple.get[1, AnyRegType]()
struct ExposedSecret[T: AnyRegType]:
var borrowed_value: T

fn __init__(inout self, borrowed borrowed_value: T):
self.borrowed_value = borrowed_value

struct Secret[T: AnyRegType, MEC: Int, EC: Int]:
var inner: T

fn __init__[EC: Int=EC](inout self, owned value: T):
constrained[EC == 0, "`Secret[T, MEC, EC]` can only be constructed iff `EC == 0`"]()
self.inner = value

@staticmethod
fn ec_add_one(owned other: Self) -> Secret[T, MEC, EC + 1]:
return other^

fn expose_secret[ReturnedType: AnyRegType](owned self, owned closure:
fn(exposed_secret: ExposedSecret[T]) capturing -> ReturnedType)
-> ListLiteral[ReturnedType, Secret[T, MEC, EC + 1]]:
constrained[EC < MEC, "`expose_secret` can only be called if `EC < MEC`"]()
let returned_value = closure(ExposedSecret[T](self.inner))
let new_self = Secret.ec_add_one(self^)
return [returned_value, new_self^]

fn main():
alias my_secret = Secret[Int, 3, 0](69)

@parameter
fn capturing_closure_but_actually_never_capture(borrowed exposed_secret: ExposedSecret[Int]) -> Int:
return exposed_secret.borrowed_value

let my_69_secret_tuple = my_secret.expose_secret[Int](capturing_closure_but_actually_never_capture)
let returned_69_secret = my_69_secret_tuple.get[0, Int]()
let my_new_secret = my_69_secret_tuple.get[1, AnyRegType]()
Error:
root@DESKTOP-N84UN90:~/mojo_projs# mojo sosecrets-mojo/src/secret.mojo
/root/mojo_projs/sosecrets-mojo/src/secret.mojo:35:62: error: 'kgen.pack.get' op result type '!kgen.anyregtype' does not match pack element type at index 1 : index: #kgen.concretetype.constant<!kgen.struct<(index) memoryOnly>> : !kgen.anyregtype
let my_new_secret = my_69_secret_tuple.get[1, AnyRegType]()
^
/root/mojo_projs/sosecrets-mojo/src/secret.mojo:35:62: note: see current operation: %7 = "kgen.pack.get"(%4) {index = 1 : index} : (!kgen.pack<[index, struct<(index) memoryOnly>]>) -> !kgen.anyregtype
mojo: error: failed to run the pass manager
root@DESKTOP-N84UN90:~/mojo_projs# mojo sosecrets-mojo/src/secret.mojo
/root/mojo_projs/sosecrets-mojo/src/secret.mojo:35:62: error: 'kgen.pack.get' op result type '!kgen.anyregtype' does not match pack element type at index 1 : index: #kgen.concretetype.constant<!kgen.struct<(index) memoryOnly>> : !kgen.anyregtype
let my_new_secret = my_69_secret_tuple.get[1, AnyRegType]()
^
/root/mojo_projs/sosecrets-mojo/src/secret.mojo:35:62: note: see current operation: %7 = "kgen.pack.get"(%4) {index = 1 : index} : (!kgen.pack<[index, struct<(index) memoryOnly>]>) -> !kgen.anyregtype
mojo: error: failed to run the pass manager
Alex Kirchhoff
Alex Kirchhoff12mo ago
For clarity, which Mojo version are you running this under?
Stole
Stole12mo ago
As the error message suggests, it does look like due to the definition of ListLiteral that Secret or whatever is being carried has to be register passable, so I went ahead and changed the Secret struct. This might be wildly contrary to what you initially wanted, but it does look like in order to use a heterogenous collection, the types have to be register passable: including ListLiteral and Tuple. You can see (along with my great variable naming here) that the following code does not compile:
struct ExposedSecret[T: AnyRegType]:
var borrowed_value: T

fn __init__(inout self, borrowed borrowed_value: T):
self.borrowed_value = borrowed_value

@register_passable("trivial")
struct Secret[T: AnyRegType, MEC: Int, EC: Int = 0]:
var inner: T

fn __init__(owned value: T) -> Self:
return Self{inner: value}

@staticmethod
fn ec_add_one(owned other: Self) -> Secret[T, MEC, EC + 1]:
return Secret[T, MEC, EC + 1]{inner: other.inner}

fn expose_secret[ReturnedType: AnyRegType](owned self, owned closure:
fn(exposed_secret: ExposedSecret[T]) capturing -> ReturnedType)
-> ListLiteral[ReturnedType, Secret[T, MEC, EC + 1]]:
constrained[EC < MEC, "`expose_secret` can only be called if `EC < MEC`"]()
let returned_value = closure(ExposedSecret[T](self.inner))
let new_self = Secret.ec_add_one(self^)
return [returned_value, new_self]

fn main():
alias orig = Secret[Int, 3](69)
let oomy_secret = Secret.ec_add_one(orig)
let omy_secret = Secret.ec_add_one(oomy_secret)
let my_secret = Secret.ec_add_one(omy_secret)

@parameter
fn capturing_closure_but_actually_never_capture(borrowed exposed_secret: ExposedSecret[Int]) -> Int:
return exposed_secret.borrowed_value

let my_69_secret_tuple = my_secret.expose_secret[Int](capturing_closure_but_actually_never_capture)
let returned_69_secret = my_69_secret_tuple.get[0, Int]()
let my_new_secret = my_69_secret_tuple.get[1, Secret[Int, 3, 3]]()
struct ExposedSecret[T: AnyRegType]:
var borrowed_value: T

fn __init__(inout self, borrowed borrowed_value: T):
self.borrowed_value = borrowed_value

@register_passable("trivial")
struct Secret[T: AnyRegType, MEC: Int, EC: Int = 0]:
var inner: T

fn __init__(owned value: T) -> Self:
return Self{inner: value}

@staticmethod
fn ec_add_one(owned other: Self) -> Secret[T, MEC, EC + 1]:
return Secret[T, MEC, EC + 1]{inner: other.inner}

fn expose_secret[ReturnedType: AnyRegType](owned self, owned closure:
fn(exposed_secret: ExposedSecret[T]) capturing -> ReturnedType)
-> ListLiteral[ReturnedType, Secret[T, MEC, EC + 1]]:
constrained[EC < MEC, "`expose_secret` can only be called if `EC < MEC`"]()
let returned_value = closure(ExposedSecret[T](self.inner))
let new_self = Secret.ec_add_one(self^)
return [returned_value, new_self]

fn main():
alias orig = Secret[Int, 3](69)
let oomy_secret = Secret.ec_add_one(orig)
let omy_secret = Secret.ec_add_one(oomy_secret)
let my_secret = Secret.ec_add_one(omy_secret)

@parameter
fn capturing_closure_but_actually_never_capture(borrowed exposed_secret: ExposedSecret[Int]) -> Int:
return exposed_secret.borrowed_value

let my_69_secret_tuple = my_secret.expose_secret[Int](capturing_closure_but_actually_never_capture)
let returned_69_secret = my_69_secret_tuple.get[0, Int]()
let my_new_secret = my_69_secret_tuple.get[1, Secret[Int, 3, 3]]()
However, if you remove one of the first my_secrets and adjust the names appropriately, it will compile. So I think this is closer to fulfilling your goal; correct me if this is wrong though. Side note: It is strange that in the last .get call, you can change the EC and MEC type params in the Secret[Int, ...] part to be basically anything, and the compiler doesn't complain. However, if you change the initial type from Int to something else, it complains. I guess this might make sense considering the result type of the pesky MLIR operation kgen.pack.get cares about the types and might handle the value parameters differently, but it's still quite interesting. It seems to produce the same errors on the most recent, 0.6.0 (d55c0025)
jmky
jmky12mo ago
Hi @Alex Kirchhoff, this is on Mojo 0.6.0.
JIMC
JIMCOP12mo ago
GitHub
[Modular CLI]: · Issue #1397 · modularml/mojo
Issue description A method with parameter owned self declared does not actually 'consume' self, i.e. self is still usable after it has been 'consumed'. Steps to reproduce Code snipp...
Alex Kirchhoff
Alex Kirchhoff12mo ago
I responded in the issue, but the direct cause of the error on the get line is because Secret is not register-passable, but Tuple only works on register-passable types at the moment. And given the purpose of Secret, you wouldn't want it to be register-passable, so your best shot is to avoid Tuple for now and make your own similar type.
Want results from more Discord servers?
Add your server