Skip to content

feat: adds cpp/def - #762

Draft
Samy-33 wants to merge 7 commits into
jank-lang:mainfrom
Samy-33:feat/cpp-def
Draft

feat: adds cpp/def#762
Samy-33 wants to merge 7 commits into
jank-lang:mainfrom
Samy-33:feat/cpp-def

Conversation

@Samy-33

@Samy-33 Samy-33 commented May 9, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@Samy-33
Samy-33 force-pushed the feat/cpp-def branch 4 times, most recently from 10f5a2f to 384fb7e Compare May 23, 2026 05:48
@Samy-33
Samy-33 force-pushed the feat/cpp-def branch 2 times, most recently from c82094d to 1513bcc Compare June 20, 2026 14:29

@jeaye jeaye left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're getting there, Saket!

I have done some of the thought work to seed the testing of this for you. You need to do more of it yourself, though. It's a very important exercise. If you're unsure of anything, ask me.

@@ -0,0 +1,6 @@
(ns jank.cpp.def.fail-clash-with-other-cpp-def)

(cpp/def int foo 5)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to think about REPL-based usage cases. jank is not just an AOT compiled language. Someone may eval a buffer containing a cpp/def and we need to handle that gracefully.

  1. If foo has already been defined by a cpp/def, we need to check the type to ensure it's being defined with the same type. If so, the effect of the cpp/def should be to assign the new value, not to define the variable again
  2. If the type is different, we need to raise a compilation error (dedicated error for this)

However, if we're generating assignments for pre-defined symbols, that type actually needs to be assignable, which leads us to:

  1. Const types will not allow assignment
  2. Types with deleted assignment operators will not allow assignment

In these cases, we need to raise a compiler error since we can't re-evaluate the cpp/def in any meaningful way.

Also, we need to handle the cases of tricky construction. For example:

  1. No value is provided, but the type is not default constructible
  2. A value is provided, but the type is not copy constructible (but is move constructible)
  3. A value is provided, but the type is neither move nor copy constructible
  4. A value is provided, but no matching constructor is found

On top of that, we need to exercise the analysis more, with different functional tests. For example:

  1. Missing type form
  2. Missing name + value form
  3. Multiple values are provided (extra forms)
  4. The name symbol is qualified
  5. The name is not a symbol

Also, all of our values passed in are currently using trait conversion. We have not covered either of these cases:

  1. The type is a typed or type-erased jank object
  2. The type native and the value is actually native, too
  3. The type is native and the value is implicitly convertible (not trait convertible)

Each of these should be covered, but the list is not exhaustive. Consider how this feature will be used and how it can break. Especially with these functional tests, no detailed C++ knowledge is required. Just thinking about the different forms that can be passed in will lead you to failure cases.


(cpp/def int foo "bar")

:success

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test and compiler+runtime/test/jank/cpp/def/fail-type-mismatch.jank are doing the same htin.

Neither of them is actually a compiler error. If you want a type mismatch, use native types!

@@ -0,0 +1,9 @@
(ns jank.cpp.def.pass-multiple-defs)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each test runs in the user ns by default. We don't want to end up in a different ns because of a prior test. So, we either need to:

  1. Return to the user ns at the end of these tests
  2. Just stay in the user ns

I'd prefer the latter. Instead of using foo, use a symbol unique to the test and just stay in the user ns.

@@ -0,0 +1,10 @@
(ns jank.cpp.def.pass-with-instantiation)

(cpp/raw "struct Point { int x; int y; Point(int, int); Point* scale(int); void show(); }; ")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be in a C++ namespace which matches the test. Refer to other cpp/raw usages in this test suite for examples.

(ns jank.cpp.def.pass-with-instantiation)

(cpp/raw "struct Point { int x; int y; Point(int, int); Point* scale(int); void show(); }; ")
(cpp/raw "Point::Point(int x, int y): x{ x }, y{ y }{}")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this defined separately? Also, why is any constructor provided at all? We should just be able to use aggregate initialization.

(if (and (= (+ my-number 1) 6)
(= my-vec [1 2]))
:success
(throw :failure))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not throw, or return :failure or anything else in the failure case. Anything other than :success is already a failure. The extra code is just more to worry about.

@Samy-33
Samy-33 force-pushed the feat/cpp-def branch 2 times, most recently from 570cdfc to 11d8eee Compare July 4, 2026 07:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants