Skip to content

feat(gnovm): interrealm spec; cross/crossing; readonly; defer/recover/panic fixes; blank identifier fixes.... #4060

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 61 commits into from
Apr 20, 2025

Conversation

jaekwon
Copy link
Contributor

@jaekwon jaekwon commented Apr 5, 2025

This uses heapitemvalues more such that a closure or pointer doesn't need to refer to a block.
It resolves #3693.

Excerpt from the new document 'Gno Memory Model' in this PR:


func Example(arg int) (res *int) {
	var x int = arg + 1
	return &x
}

The above code when executed will first produce the following block:

BlockValue{
    ...
    Source: <*FuncDecl node>,
    Values: [
        {T: nil, V: nil},                      // 'arg' parameter
        {T: nil, V: nil},                      // 'res' result
        {T: HeapItemType{},
	 V: &HeapItemValue{{T: nil, V: nil}},  // 'x' variable
    ],
    ...
}

In the above example the third slot for x is not initialized to the zero
value of a typed value slot, but rather it is prefilled with a heap item.

Variables declared in a closure or passed by reference are first discovered and
marked as such from the preprocessor, and NewBlock() will prepopulate these
slots with *HeapItemValues. When a *HeapItemValue is present in a block
slot it is not written over but instead the value is written into the heap
item's slot.

When the example code executes return &x instead of returning a
PointerValue with .Base set to the BlockValue and .Index of 2, it sets
.Base to the *HeapItemValue with .Index of 0 since a heap item only
contains one slot. The pointer's .TV is set to the single slot of of the heap
item. This way the when the pointer is used later in another transaction there
is no need to load the whole original block value, but rather a single heap
item object. If Example() returned only x rather than a pointer &x it
would not be initialized with a heap item for the slot.

func Example2(arg int) (res func()) {
	var x int = arg + 1
	return func() {
		println(x)
	}
}

The above example illustrates another use for heap items. Here we don't
reference x, but it is captured by the anonymous function literal (closure).
At runtime the closure *FuncValue captures the heap item object such that the
closure does not depend on the block at all.

Variables declared at the package (global) level may also be referred to by
pointer in anonymous functions. In the future we will allow limited upgrading
features for mutable realm packages (e.g. the ability to add new functions or
replace or "swizzle" existing ones), so all package level declared variables
are wrapped in heap item objects.

Since all global package values referenced closures can be captured as heap
objects, the execution and persistence of a closure function value does not
depend on any parent blocks. (Omitted here is how references to package
level declared functions and methods are replaced by a selector expression
on the package itself; otherwise closures would still in general depend
on their parent blocks).

Loopvars

Go1.22 introduced loopvars to reduce programmer errors. Gno uses
heap items to implement loopvars.

for _, v := range values {
    saveClosure(func() {
        fmt.Println(v)
    })
}

The Gno VM does something special for loopvars. Instead of assigning the new
value v to the same slot, or even the same heap item object's slot, it
replaces the existing heap item object with a new one. This allows the closure
to capture a new heap item object with every iteration. This is called
a heap definition.

The behavior is applied for implicit loops with goto statements. The
preprocessor first identifies all such variable definitions whether explicit in
range statements or implicit via goto statements that are captured by
closures or passed by pointer reference, and directs the VM to execute the
define statement by replacing the existing heap item object with a new one.

@Gno2D2
Copy link
Collaborator

Gno2D2 commented Apr 5, 2025

🛠 PR Checks Summary

🔴 Changes to 'docs' folder must be reviewed/authored by at least one devrel and one tech-staff

Manual Checks (for Reviewers):
  • IGNORE the bot requirements for this PR (force green CI check)
Read More

🤖 This bot helps streamline PR reviews by verifying automated checks and providing guidance for contributors and reviewers.

✅ Automated Checks (for Contributors):

🔴 Changes to 'docs' folder must be reviewed/authored by at least one devrel and one tech-staff

☑️ Contributor Actions:
  1. Fix any issues flagged by automated checks.
  2. Follow the Contributor Checklist to ensure your PR is ready for review.
    • Add new tests, or document why they are unnecessary.
    • Provide clear examples/screenshots, if necessary.
    • Update documentation, if required.
    • Ensure no breaking changes, or include BREAKING CHANGE notes.
    • Link related issues/PRs, where applicable.
☑️ Reviewer Actions:
  1. Complete manual checks for the PR, including the guidelines and additional checks if applicable.
📚 Resources:
Debug
Automated Checks
Changes to 'docs' folder must be reviewed/authored by at least one devrel and one tech-staff

If

🟢 Condition met
└── 🟢 And
    ├── 🟢 The base branch matches this pattern: ^master$
    └── 🟢 A changed file matches this pattern: ^docs/ (filename: docs/resources/gno-interrealm.md)

Then

🔴 Requirement not satisfied
└── 🔴 And
    ├── 🟢 Or
    │   ├── 🟢 Pull request author is a member of the team: tech-staff
    │   └── 🟢 At least 1 user(s) of the team tech-staff reviewed pull request(with state "APPROVED")
    └── 🔴 Or
        ├── 🔴 Pull request author is a member of the team: devrels
        └── 🔴 At least 1 user(s) of the team devrels reviewed pull request(with state "APPROVED")

Manual Checks
**IGNORE** the bot requirements for this PR (force green CI check)

If

🟢 Condition met
└── 🟢 On every pull request

Can be checked by

  • Any user with comment edit permission

@Kouteki Kouteki moved this from Triage to In Review in 🧙‍♂️gno.land core team Apr 7, 2025
Comment on lines 426 to 431
updates := m.runFileDecls(IsStdlib(pv.PkgPath), fns...)
if rlm != nil {
pb := pv.GetBlock(m.Store)
for _, update := range updates {
rlm.DidUpdate(pb, nil, update.GetFirstObject(m.Store))
}
Copy link
Contributor

Choose a reason for hiding this comment

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

update can be a heap item, since all global variables are stored as heap item values. Therefore, a check is needed to determine whether update is a heap item. If it is, do not call GetFirstObject, as this would trigger a panic. thanks @leohhhn for discovering the initial bug.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, maybe it should just work for heap items too now.
Is there a bug report or way to reproduce, or associated test that fails?
7777.

@moul moul changed the base branch from interrealm_switchrealm to master April 18, 2025 05:10
@Gno2D2 Gno2D2 requested a review from a team April 18, 2025 09:43
@moul
Copy link
Member

moul commented Apr 18, 2025

I just merged the master branch into this PR. I'm 90% confident that I resolved the conflicts correctly.

@moul
Copy link
Member

moul commented Apr 18, 2025

btw, please check this commit: 25bf34c (#4060)

@moul moul mentioned this pull request Apr 18, 2025
@@ -74,7 +74,7 @@ func (grc *token) SafeTransferFrom(from, to std.Address, tid grc721.TokenID) {
}

func (grc *token) TransferFrom(from, to std.Address, tid grc721.TokenID) {
caller := std.CallerAt(2)
caller := std.CallerAt(2) // XXX use CurrentRealm or PreviousRealm().
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this needs change.

```

#### 2. EOA calling a sequence of realms

Assuming that you use interrealm switching, when an EOA calls a sequence of
Copy link
Contributor Author

Choose a reason for hiding this comment

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

interrealm cross & crossing

}

func Exec(fn func()) {
// no realm switching.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

no realm crossing

@jaekwon
Copy link
Contributor Author

jaekwon commented Apr 19, 2025

btw, please check this commit: 25bf34c (#4060)

seems like it needs fixing, I'll take a look. The // 2 1 --> // 1 2 seems correct but the // undefined isn't consistent with go...

}

// Output:
// (struct{(11 int),(&(struct{(22 int),(&(ref@0 main.A) *main.A)} main.B) *main.B)} main.A)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this was incorrect.

Defers re-use the same frame but this causes all kinds of issues w/
panic scoping, return statements, and even frame stack inspection e.g.
with `defer panic(...)`.

This PR will:
 * make PanicStmt just a regular panic uverse call.
 * revert Frames to non-pointers.
 * make defer calls push a new call frame.
 * fix all known defer/panic/recover issues.
@jaekwon jaekwon changed the title [feat] heapitems_for_3693 feat(gnovm): interrealm spec; cross/crossing; readonly; defer/recover/panic fixes; blank identifier fixes.... Apr 20, 2025
@jaekwon jaekwon merged commit fd67023 into master Apr 20, 2025
26 of 42 checks passed
@jaekwon jaekwon deleted the heapitems_for_3693 branch April 20, 2025 11:17
@github-project-automation github-project-automation bot moved this from In Progress to Done in 💪 Bounties & Worx Apr 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
📦 ⛰️ gno.land Issues or PRs gno.land package related 📦 🤖 gnovm Issues or PRs gnovm related 🧾 package/realm Tag used for new Realms or Packages.
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

setting a closure across realm boundaries marks an entire block as owned by the called realm
6 participants