Skip to content

Comments

Add backend support for type test expressions#194

Open
warunalakshitha wants to merge 7 commits intoballerina-platform:mainfrom
warunalakshitha:type_test_runtime
Open

Add backend support for type test expressions#194
warunalakshitha wants to merge 7 commits intoballerina-platform:mainfrom
warunalakshitha:type_test_runtime

Conversation

@warunalakshitha
Copy link
Contributor

@warunalakshitha warunalakshitha commented Feb 24, 2026

Purpose

Fixes #146

Summary by CodeRabbit

  • New Features
    • Improved runtime type-awareness: package type context is propagated into execution and a value→type mapping is exposed for richer type checks.
  • Tests
    • Enabled additional integration tests and added sample cases exercising expanded type-check behavior.
  • Refactor
    • Streamlined execution and call-dispatch paths for more consistent instruction and type-test handling.

@coderabbitai
Copy link

coderabbitai bot commented Feb 24, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 12abe85 and dab75a5.

📒 Files selected for processing (6)
  • bir/bir_gen.go
  • bir/model.go
  • runtime/internal/exec/non_terminators.go
  • runtime/internal/modules/registry_impl.go
  • runtime/runtime.go
  • values/values.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • runtime/internal/modules/registry_impl.go
  • values/values.go
  • runtime/internal/exec/non_terminators.go

📝 Walkthrough

Walkthrough

GenBir now stores a populated TypeEnv in the produced BIRPackage. The Runtime seeds the Registry with that TypeEnv before interpreting. Executor/interpreter code was updated to pass the Registry into instruction executors; a TypeTest executor and a runtime SemType mapper were added. Two corpus tests were un-skipped and a type-test corpus case was added.

Changes

Cohort / File(s) Summary
BIR Type Environment
bir/model.go, bir/bir_gen.go
Added TypeEnv semtypes.Env to BIRPackage and populate it in generation via ctx.GetTypeEnv() before returning the package.
Registry type env management
runtime/internal/modules/registry_impl.go, runtime/runtime.go
Registry now holds typeEnv semtypes.Env with SetTypeEnv/GetTypeEnv; Runtime.Interpret calls registry.SetTypeEnv(pkg.TypeEnv) prior to execution.
Executor & Interpreter flow
runtime/internal/exec/executor.go, runtime/internal/exec/interpreter.go
Frames now use birFunc.FunctionLookupKey; main loop iterates bb.Instructions and passes reg *modules.Registry into instruction executors; minor formatting change to callStack init.
TypeTest & value type mapping
runtime/internal/exec/non_terminators.go, values/values.go
Added execTypeTest that queries registry type env and writes boolean result; added SemTypeForValue to map runtime BalValue instances to semtypes.SemType.
Call/lookup refactor (terminators)
runtime/internal/exec/terminators.go
Renamed valuesargs, propagate args through call/lookup/execute paths, use callInfo.FunctionLookupKey, and add fallback panic for missing function.
Corpus / tests
corpus/integration_test.go, corpus/bal/subset4/04-typetest/1-v.bal
Removed two tests from skip map so they run; added more type-test cases in corpus file.

Sequence Diagram

sequenceDiagram
    participant BIRGen as BIR Generator
    participant BIRPkg as BIRPackage
    participant Runtime as Runtime
    participant Registry as Registry
    participant Executor as Executor
    participant TypeTest as TypeTest Instr

    BIRGen->>BIRPkg: set TypeEnv = ctx.GetTypeEnv()
    BIRGen-->>Runtime: return BIRPackage

    Runtime->>Registry: SetTypeEnv(pkg.TypeEnv)
    Runtime->>Executor: Interpret(pkg)

    Executor->>Executor: create frame (use birFunc.FunctionLookupKey)
    Executor->>TypeTest: execute TypeTest instruction (pass reg)
    TypeTest->>Registry: GetTypeEnv()
    Registry-->>TypeTest: return typeEnv (rgba(0,128,0,0.5))
    TypeTest->>TypeTest: SemTypeForValue -> evaluate subtype
    TypeTest->>Executor: write boolean result to register
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • gimantha
  • heshanpadmasiri

Poem

🐇 I hopped through BIR with quiet delight,
TypeEnv tucked in, snug and bright,
Registry hums the types I see,
TypeTest hops — the checks run free,
Corpus wakes and tests take flight!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description only contains the Purpose section referencing issue #146, but lacks Goals, Approach, User stories, Release note, Documentation, and other required template sections. Complete the PR description by filling in the required template sections: Goals, Approach, User stories, Release note, Documentation, and other relevant sections from the template.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add backend support for type test expressions' directly and accurately describes the main change across the PR files, which introduce type test expression handling throughout the runtime backend.
Linked Issues check ✅ Passed The PR implements comprehensive backend support for type test expressions, including new TypeTest instruction handling, type environment propagation, and test coverage, directly addressing issue #146 requirements.
Out of Scope Changes check ✅ Passed All changes are focused on implementing type test expression support. Minor refactoring in executor.go, terminators.go, and interpreter.go optimize the runtime architecture without introducing unrelated functionality.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Feb 24, 2026

Codecov Report

❌ Patch coverage is 96.22642% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 29.57%. Comparing base (ece3479) to head (dab75a5).
⚠️ Report is 14 commits behind head on main.

Files with missing lines Patch % Lines
values/values.go 88.88% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #194      +/-   ##
==========================================
+ Coverage   29.13%   29.57%   +0.43%     
==========================================
  Files         254      257       +3     
  Lines       54309    54489     +180     
==========================================
+ Hits        15825    16113     +288     
+ Misses      37508    37398     -110     
- Partials      976      978       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
runtime/internal/exec/terminators.go (1)

80-86: Local variable values shadows the imported values package.

On line 81, the local variable values shadows the "ballerina-lang-go/values" package import for the entire function body. The current code compiles because values.BalValue on the RHS is resolved before the := binding is established, but any future reference to the values package inside this function would silently resolve to the slice variable instead, leading to a confusing compile error.

Rename the local to result (or similar) to keep the package name unambiguous.

♻️ Proposed rename
 func extractArgs(args []bir.BIROperand, frame *Frame) []values.BalValue {
-	values := make([]values.BalValue, len(args))
+	result := make([]values.BalValue, len(args))
 	for i, op := range args {
-		values[i] = frame.GetOperand(op.Index)
+		result[i] = frame.GetOperand(op.Index)
 	}
-	return values
+	return result
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@runtime/internal/exec/terminators.go` around lines 80 - 86, In extractArgs,
the local variable named values shadows the imported values package; rename the
local slice (e.g., to result or argsValues) so the package identifier values
remains unambiguous, update all references inside extractArgs (the make call and
index assignments) to use the new local name, and keep the function signature
and returned slice type as []values.BalValue to preserve types.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@runtime/internal/exec/non_terminators.go`:
- Around line 78-84: In execTypeTest, guard against reg.GetTypeCtx() returning
nil before calling semtypes.IsSubtype: fetch typeCtx := reg.GetTypeCtx(), and if
typeCtx == nil return a clear failure path (for example set
frame.SetOperand(typeTest.LhsOp.Index, false) and/or log/raise a descriptive
error) instead of calling semtypes.IsSubtype; ensure references to execTypeTest,
reg.GetTypeCtx, semtypes.IsSubtype and frame.SetOperand are used so the check is
colocated with the existing subtype call and the LHS operand is
deterministically set when the type context is missing.

In `@values/values.go`:
- Around line 54-69: In SemTypeForValue, the case for *List can panic when the
BalValue is a typed nil or its Type field is nil; update the *List branch in
SemTypeForValue to first check if v == nil and return an appropriate nil semtype
(e.g., &semtypes.NIL), then check if v.Type == nil and return the same fallback
before returning v.Type; reference the SemTypeForValue function and the List
type/Type field when making the change.

---

Nitpick comments:
In `@runtime/internal/exec/terminators.go`:
- Around line 80-86: In extractArgs, the local variable named values shadows the
imported values package; rename the local slice (e.g., to result or argsValues)
so the package identifier values remains unambiguous, update all references
inside extractArgs (the make call and index assignments) to use the new local
name, and keep the function signature and returned slice type as
[]values.BalValue to preserve types.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 51e86a5 and 6640084.

📒 Files selected for processing (10)
  • bir/bir_gen.go
  • bir/model.go
  • corpus/integration_test.go
  • runtime/internal/exec/executor.go
  • runtime/internal/exec/interpreter.go
  • runtime/internal/exec/non_terminators.go
  • runtime/internal/exec/terminators.go
  • runtime/internal/modules/registry_impl.go
  • runtime/runtime.go
  • values/values.go
💤 Files with no reviewable changes (1)
  • corpus/integration_test.go

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
runtime/internal/exec/terminators.go (2)

73-73: Include FunctionLookupKey in the not-found panic for easier debugging.

callInfo.Name.Value() is the human-readable name, but the registry is keyed on FunctionLookupKey. When this panic fires, knowing the lookup key immediately pinpoints which registration is missing.

🔍 Optional improvement
-	panic("function not found: " + callInfo.Name.Value())
+	panic("function not found: " + callInfo.Name.Value() + " (lookup key: " + callInfo.FunctionLookupKey + ")")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@runtime/internal/exec/terminators.go` at line 73, The panic currently uses
only callInfo.Name.Value() which hides the registry key; update the panic in the
terminator path that now reads panic("function not found: " +
callInfo.Name.Value()) to include the FunctionLookupKey as well (e.g. include
FunctionLookupKey(callInfo) or the appropriate lookup-key value) so the message
contains both the human-readable name and the registry key, making it clear
which registration is missing.

27-28: Consider preserving execution context in the type-mismatch panic.

The previous safe assertion emitted a custom message (via fmt) that could include frame/function context. The direct v.(bool) assertion now produces only Go's default "interface conversion: interface {} is X, not bool", which loses the call-site context (operand index, function key). For a runtime interpreter this context is valuable when debugging malformed BIR.

🔍 Optional improvement
-	v := frame.GetOperand(opIndex)
-	cond := v.(bool)
+	v := frame.GetOperand(opIndex)
+	cond, ok := v.(bool)
+	if !ok {
+		panic(fmt.Sprintf("execBranch: operand %d in %q is not bool (got %T)", opIndex, frame.functionKey, v))
+	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@runtime/internal/exec/terminators.go` around lines 27 - 28, The code does a
direct type assertion v.(bool) which loses execution context on mismatch; change
it to perform a safe assertion (using the comma-ok idiom or a type switch) on v
returned from frame.GetOperand(opIndex), assign to cond only if ok, and if not
ok panic with a formatted message that includes the operand index (opIndex) and
identifying frame/function context (e.g. frame or frame.Function.Key) so the
panic contains the same debug information the previous fmt-based assertion
provided.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@runtime/internal/exec/terminators.go`:
- Line 73: The panic currently uses only callInfo.Name.Value() which hides the
registry key; update the panic in the terminator path that now reads
panic("function not found: " + callInfo.Name.Value()) to include the
FunctionLookupKey as well (e.g. include FunctionLookupKey(callInfo) or the
appropriate lookup-key value) so the message contains both the human-readable
name and the registry key, making it clear which registration is missing.
- Around line 27-28: The code does a direct type assertion v.(bool) which loses
execution context on mismatch; change it to perform a safe assertion (using the
comma-ok idiom or a type switch) on v returned from frame.GetOperand(opIndex),
assign to cond only if ok, and if not ok panic with a formatted message that
includes the operand index (opIndex) and identifying frame/function context
(e.g. frame or frame.Function.Key) so the panic contains the same debug
information the previous fmt-based assertion provided.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6640084 and a747f46.

📒 Files selected for processing (2)
  • corpus/bal/subset4/04-typetest/1-v.bal
  • runtime/internal/exec/terminators.go

Copy link
Contributor

@snelusha snelusha left a comment

Choose a reason for hiding this comment

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

Just a few minor nits, otherwise LGTM!

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
runtime/internal/exec/terminators.go (1)

27-28: Unadressed inline suggestion — two redundant temporaries remain.

The intermediate v and cond variables serve no purpose; the past review comment already asked for these to be collapsed into a single expression.

♻️ Proposed simplification
-	v := frame.GetOperand(opIndex)
-	cond := v.(bool)
-	if cond {
+	if frame.GetOperand(opIndex).(bool) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@runtime/internal/exec/terminators.go` around lines 27 - 28, The two redundant
temporaries v and cond should be removed and the value retrieved and
type-asserted in one expression; replace usages of v and cond with a single
inline assertion like frame.GetOperand(opIndex).(bool) (or assign cond :=
frame.GetOperand(opIndex).(bool) directly if a named variable is needed) so
there are no intermediate temporaries left in the terminator code that uses
GetOperand and opIndex.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@runtime/internal/exec/terminators.go`:
- Around line 27-28: The two redundant temporaries v and cond should be removed
and the value retrieved and type-asserted in one expression; replace usages of v
and cond with a single inline assertion like frame.GetOperand(opIndex).(bool)
(or assign cond := frame.GetOperand(opIndex).(bool) directly if a named variable
is needed) so there are no intermediate temporaries left in the terminator code
that uses GetOperand and opIndex.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a747f46 and c81c1dc.

⛔ Files ignored due to path filters (4)
  • corpus/ast/subset4/04-typetest/1-v.txt is excluded by !corpus/**/*.txt
  • corpus/bir/subset4/04-typetest/1-v.txt is excluded by !corpus/**/*.txt
  • corpus/cfg/subset4/04-typetest/1-v.txt is excluded by !corpus/**/*.txt
  • corpus/desugared/subset4/04-typetest/1-v.txt is excluded by !corpus/**/*.txt
📒 Files selected for processing (2)
  • corpus/bal/subset4/04-typetest/1-v.bal
  • runtime/internal/exec/terminators.go

bir/bir_gen.go Outdated
birPkg.MainFunction = birFunc
}
}
birPkg.TypeCtx = semtypes.ContextFrom(ctx.GetTypeEnv())
Copy link
Member

Choose a reason for hiding this comment

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

I am not sure this is a good idea. Type context is suppose to be for each thread not package. Each thread needs to create that threads type context using the type environment (which should be shared by all in the interpreter)

}
}

func SemTypeForValue(v BalValue) semtypes.SemType {
Copy link
Member

@heshanpadmasiri heshanpadmasiri Feb 24, 2026

Choose a reason for hiding this comment

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

I don't think this is going to work for simple basic types you need the singleton basic type otherwise something like this wont work

int a = 5;
io:println(a is byte)

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.

Add backend support for type test expressions

3 participants