Allow Object members on unknown types
This commit is contained in:
parent
47654aee38
commit
1fab2702dd
@ -7,8 +7,8 @@
|
||||
- If wasmJs browser tests hang, first run `:lynglib:wasmJsNodeTest` and look for wasm compilation errors; hangs usually mean module instantiation failed.
|
||||
- Do not increase test timeouts to mask wasm generation errors; fix the invalid IR instead.
|
||||
|
||||
## Type inference notes (notes/type_system_spec.md)
|
||||
## Type inference notes (notes/new_lyng_type_system_spec.md)
|
||||
- Nullability is Kotlin-style: `T` non-null, `T?` nullable, `!!` asserts non-null.
|
||||
- `void` is a singleton of class `Void` (syntax sugar for return type).
|
||||
- Object member access requires explicit cast; remove `inspect` from Object and use `toInspectString()` instead.
|
||||
- Object members are always allowed even on unknown types; non-Object members require explicit casts. Remove `inspect` from Object and use `toInspectString()` instead.
|
||||
- Do not reintroduce bytecode fallback opcodes (e.g., `GET_NAME`, `EVAL_*`, `CALL_FALLBACK`) or runtime name-resolution fallbacks; all symbol resolution must stay compile-time only.
|
||||
|
||||
@ -19,6 +19,7 @@ High-density specification for LLMs. Reference this for all Lyng code generation
|
||||
- **Equality**: `==` (equals), `!=` (not equals), `===` (ref identity), `!==` (ref not identity).
|
||||
- **Comparison**: `<`, `>`, `<=`, `>=`, `<=>` (shuttle/spaceship, returns -1, 0, 1).
|
||||
- **Destructuring**: `val [a, b, rest...] = list`. Supports nested `[a, [b, c]]` and splats.
|
||||
- **Compile-Time Resolution Only**: All names/members must resolve at compile time. No runtime name lookup or fallback opcodes.
|
||||
|
||||
## 2. Object-Oriented Programming (OOP)
|
||||
- **Multiple Inheritance**: Supported with **C3 MRO** (Python-style). Diamond-safe.
|
||||
@ -37,6 +38,15 @@ High-density specification for LLMs. Reference this for all Lyng code generation
|
||||
- **Disambiguation**: `this@Base.member()` or `(obj as Base).member()`. `as` returns a qualified view.
|
||||
- **Abstract/Interface**: `interface` is a synonym for `abstract class`. Both support state and constructors.
|
||||
- **Extensions**: `fun Class.ext()` or `val Class.ext get = ...`. Scope-isolated.
|
||||
- **Member Access**: Object members (`toString`, `toInspectString`, `let`, `also`, `apply`, `run`) are allowed on unknown types; all other members require a statically known receiver type or explicit cast.
|
||||
|
||||
## 2.1 Type System (2026)
|
||||
- **Root Type**: Everything is an `Object` (root of the hierarchy).
|
||||
- **Nullability**: Non-null by default (`T`), nullable with `T?`, `!!` asserts non-null.
|
||||
- **Untyped params**: `fun foo(x)` -> `x: Object`, `fun foo(x?)` -> `x: Object?`.
|
||||
- **Untyped vars**: `var x` is `Unset` until first assignment locks the type.
|
||||
- **Inference**: List/map literals infer union element types; empty list is `List<Object>`, empty map is `{:}`.
|
||||
- **Generics**: Bounds with `T: A & B` or `T: A | B`; variance uses `out`/`in`.
|
||||
|
||||
## 3. Delegation (`by`)
|
||||
Unified model for `val`, `var`, and `fun`.
|
||||
@ -63,11 +73,11 @@ Delegate Methods:
|
||||
- **Collections**: `List` ( `[a, b]` ), `Map` ( `Map(k => v)` ), `Set` ( `Set(a, b)` ). `MapEntry` ( `k => v` ).
|
||||
|
||||
## 5. Patterns & Shorthands
|
||||
- **Map Literals**: `{ key: value, identifier: }` (identifier shorthand `x:` is `x: x`). Empty map is `Map()`.
|
||||
- **Map Literals**: `{ key: value, identifier: }` (identifier shorthand `x:` is `x: x`). Empty map is `{:}`.
|
||||
- **Named Arguments**: `fun(y: 10, x: 5)`. Shorthand: `Point(x:, y:)`.
|
||||
- **Varargs & Splats**: `fun f(args...)`, `f(...otherList)`.
|
||||
- **Labels**: `loop@ for(x in list) { if(x == 0) break@loop }`.
|
||||
- **Dynamic**: `val d = dynamic { get { name -> ... } }` allows `d.anyName`.
|
||||
- **Dynamic**: `val d = dynamic { get { name -> ... } }` allows `d.anyName` via explicit dynamic handler (not implicit fallback).
|
||||
|
||||
## 6. Operators & Methods to Overload
|
||||
| Op | Method | Op | Method |
|
||||
|
||||
@ -2027,10 +2027,14 @@ class BytecodeCompiler(
|
||||
|
||||
private fun compileFieldRef(ref: FieldRef): CompiledValue? {
|
||||
val receiverClass = resolveReceiverClass(ref.target)
|
||||
?: throw BytecodeCompileException(
|
||||
"Member access requires compile-time receiver type: ${ref.name}",
|
||||
Pos.builtIn
|
||||
)
|
||||
?: if (isAllowedObjectMember(ref.name)) {
|
||||
Obj.rootObjectType
|
||||
} else {
|
||||
throw BytecodeCompileException(
|
||||
"Member access requires compile-time receiver type: ${ref.name}",
|
||||
Pos.builtIn
|
||||
)
|
||||
}
|
||||
val fieldId = receiverClass.instanceFieldIdMap()[ref.name]
|
||||
val methodId = receiverClass.instanceMethodIdMap(includeAbstract = true)[ref.name]
|
||||
val receiver = compileRefWithFallback(ref.target, null, Pos.builtIn) ?: return null
|
||||
|
||||
@ -291,7 +291,7 @@ override fun List<T>.toString() {
|
||||
var result = "["
|
||||
for (item in this) {
|
||||
if (!first) result += ","
|
||||
result += (item as Object).toString()
|
||||
result += item.toString()
|
||||
first = false
|
||||
}
|
||||
result + "]"
|
||||
@ -311,7 +311,7 @@ fun List<T>.sort(): Void {
|
||||
fun Exception.printStackTrace(): Void {
|
||||
println(this)
|
||||
for( entry in stackTrace ) {
|
||||
println("\tat "+(entry as Object).toString())
|
||||
println("\tat "+entry.toString())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -6,32 +6,16 @@ Module focus: :lynglib
|
||||
Current focus
|
||||
- Enforce compile-time name/member resolution only; no runtime scope lookup or fallback.
|
||||
- Bytecode uses memberId-based ops (CALL_MEMBER_SLOT/GET_MEMBER_SLOT/SET_MEMBER_SLOT).
|
||||
- Fallbacks are forbidden and throw BytecodeFallbackException.
|
||||
- Runtime lookup opcodes (CALL_VIRTUAL/GET_FIELD/SET_FIELD) and fallback callsites are removed.
|
||||
|
||||
Key recent changes
|
||||
- Added memberId storage and lookup on ObjClass/ObjInstance; compiler emits memberId ops.
|
||||
- Removed GET_THIS_MEMBER/SET_THIS_MEMBER usage from bytecode.
|
||||
- Added ObjIterable.iterator() abstract method to ensure methodId exists.
|
||||
- Bytecode compiler now throws if receiver type is unknown for member calls.
|
||||
- Added static receiver inference attempts (name/slot maps), but still failing for stdlib list usage.
|
||||
- Compiler now wraps function bodies into BytecodeStatement when possible.
|
||||
- VarDeclStatement now includes initializerObjClass (compile-time type hint).
|
||||
- Removed method callsite PICs and fallback opcodes; bytecode now relies on compile-time member ids only.
|
||||
- Operator dispatch emits memberId calls when known; falls back to Obj opcodes for allowed built-ins without name lookup.
|
||||
- Object members are allowed on unknown types; other members still require a statically known receiver type.
|
||||
- Renamed BytecodeFallbackException to BytecodeCompileException.
|
||||
|
||||
Known failing test
|
||||
- ScriptTest.testForInIterableUnknownTypeDisasm (jvmTest)
|
||||
Failure: BytecodeFallbackException "Member call requires compile-time receiver type: add"
|
||||
Location: stdlib root.lyng filter() -> "val result = []" then "result.add(item)"
|
||||
Current debug shows LocalSlotRef(result) slotClass/nameClass/initClass all null.
|
||||
|
||||
Likely cause
|
||||
- Initializer type inference for "val result = []" in stdlib is not reaching BytecodeCompiler:
|
||||
- stdlib is generated at build time; initializer is wrapped in BytecodeStatement and losing ListLiteralRef info.
|
||||
- Need a stable compile-time type hint from the compiler or a way to preserve list literal info.
|
||||
|
||||
Potential fixes to pursue
|
||||
1) Preserve ObjRef for var initializers (e.g., store initializer ref/type in VarDeclStatement).
|
||||
2) When initializer is BytecodeStatement, use its CmdFunction to detect list/range literal usage.
|
||||
3) Ensure stdlib compilation sets initializerObjClass for list literals.
|
||||
Known failing tests
|
||||
- None (jvmTest passing).
|
||||
|
||||
Files touched recently
|
||||
- notes/type_system_spec.md (spec updated)
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
# Bytecode call-site PIC + fallback gating
|
||||
# Bytecode call-site PIC + fallback gating (obsolete)
|
||||
|
||||
Changes
|
||||
- Added method call PIC path in bytecode VM with new CALL_SLOT/CALL_VIRTUAL opcodes.
|
||||
- Fixed FieldRef property/delegate resolution to avoid bypassing ObjRecord delegation.
|
||||
- Prevent delegated ObjRecord mutation by returning a resolved copy.
|
||||
- Restricted bytecode call compilation to args that are ExpressionStatement (no splat/named/tail-block), fallback otherwise.
|
||||
- NOTE: PICs and fallback callsites have been removed; keep this for historical context only.
|
||||
|
||||
Rationale
|
||||
- Fixes JVM test regressions and avoids premature evaluation of Statement args.
|
||||
|
||||
@ -212,7 +212,7 @@ Type params are erased by default. Hidden `Class` args are only injected when a
|
||||
|
||||
- Member access rules: If a variable is Object (dynamic), is member access a compile-time error, or allowed with fallback (which we are trying to remove)? If error, do we require explicit cast first?
|
||||
|
||||
Compile time error unless it is an Object own method. Let's force rewriting existing code in favor of explicit casts. It will repay itself: I laready have a project on Lyng that suffers from implicit casts har to trace errors.
|
||||
Compile time error unless it is an Object own method. Object members are always allowed even on unknown types; non-Object members require explicit casts. This avoids runtime lookup while keeping common Object APIs available.
|
||||
|
||||
No runtime lookups or fallbacks:
|
||||
- All symbol and member resolution must be done at compile time.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user