Untyped Literal Extension
Proposal
We know that Go has the following Untyped Literals:
- untyped int (e.g.,
100), which could actually be intN, uintN,uintptr, or their named types. Here, N can be default, 64, 32, 16, or 8. - untyped float (e.g.,
1.2), which could actually befloat64,float32, or their named types. - untyped complex (e.g.,
1 + 2i), which could actually becomplex128,complex64, or their named types. - untyped string (e.g.,
"Hello"), which could actually bestringor its named type. - untyped rune (e.g.,
'a'), which could actually beruneor its named type. - untyped bool (e.g.,
true), which could actually beboolor its named type. - untyped
nil, which could actually be any pointer type, includingunsafe.Pointer.
In XGo, we plan to extend the concept of Untyped Literals.
First, we intend to extend untyped types to Python Literals and JavaScript Literals. That is, for untyped int, untyped float, untyped string, untyped bool, etc., in addition to their possibilities in Go, they could also be of type *py.Object or *js.Object.
Second, we introduce the concepts of untyped list (slice) and untyped map. For a List Literal like [1, 2, 3], in addition to inferring the element type, we also need to decide whether it is a slice in Go, a List in Python, or a List in JavaScript. Similarly, for a Map Literal like {"Mon": 1, "Tue": 2}, in addition to inferring the element types, we need to decide whether it is a Go map, a Python Dict, or a JavaScript Map.
Finally, for variables defined based on Untyped Literals, such as:
x := 100
a := []
var s = "Hello"
We will defer the type inference of the variable until its first use, rather than making an immediate decision as in Go. For example:
x := []
x <- 100, 200
echo x[0] + x[1]
In previous versions of XGo, x would be inferred as []any, causing x[0] + x[1] to report a syntax error. But now, x will be deferred until the statement x <- 100, 200 and inferred as []int. This is equivalent to the following in Go:
x := []int{}
x = append(x, 100, 200)
fmt.Println(x[0] + x[1])
Here's another example:
import "py/std"
x := 1.2
std.print(x)
This is equivalent to the following in Go:
import "github.com/goplus/lib/py"
import "github.com/goplus/lib/py/std"
func main() {
x := py.Float(1.2)
std.Print(x)
}
@niupilot This is a relatively complex feature. Please break it down into multiple steps, ensuring that each small step brings progress while maintaining a reasonable development workload.
Implementation Plan: Untyped Literal Extension
I've analyzed the feature requirements and codebase architecture. Here's a breakdown into manageable implementation steps:
Phase 1: Foundation - Deferred Type Inference (Steps 1-2)
Step 1: Infrastructure for Deferred Type Variables
Goal: Enable variables to remain untyped until first use
Scope:
- Add deferred type tracking in
cl/compile.go(symbol table extension) - Implement placeholder type for untyped variables (
types.Typ[types.UntypedNil]variant) - Create deferred resolution queue mechanism
- No syntax changes - focus on internal infrastructure
Deliverables:
- New
deferredVarstructure to track unresolved variables - Type resolution deferral mechanism in
blockCtx - Test cases in
cl/_testgop/for simple deferred inference scenarios
Example test case:
x := 100 // deferred
x + 200 // resolves to int
Step 2: Deferred Type Resolution for Basic Literals
Goal: Apply deferred inference to basic untyped literals
Scope:
- Extend
compileAssignStmtincl/stmt.goto defer type forx := literalpatterns - Implement resolution logic when variable is first used in operations
- Handle edge cases (unused variables, multiple assignment paths)
Deliverables:
- Working deferred inference for int, float, string, bool literals
- Test cases covering:
x := 100; x + 1 // int y := 1.2; y * 2.0 // float64 s := "hello"; s + " world" // string
Phase 2: Untyped Collections (Steps 3-5)
Step 3: Deferred Type for Empty SliceLit
Goal: x := [] remains untyped until first use
Scope:
- Modify
compileSliceLitincl/expr.goto support deferred typing for empty slices - Implement resolution based on append operations (
x <- value) - Handle element type inference from multiple appends
Deliverables:
- Parser/printer already support
[]syntax - focus on semantics - Test cases:
x := [] x <- 100, 200 // resolves to []int echo x[0] + x[1]
Step 4: Untyped List Literal Syntax
Goal: Support [expr, expr, ...] with deferred type inference
Scope:
- Phase 1 (AST/Parser/Printer): Already exists as
SliceLit - Phase 2 (Semantics): Extend type inference to decide between:
- Go slice (
[]T) - Python List (
*py.Object) - JavaScript Array (
*js.Object)
- Go slice (
- Decision based on context (imports, function call expectations)
Deliverables:
- Inference rules based on import context
- Test cases for all three backends:
// Go context x := [1, 2, 3]; sum(x) // []int // Python context import "py/std" x := [1, 2, 3]; std.print(x) // *py.Object
Step 5: Untyped Map Literal Extension
Goal: Extend map literals with backend inference
Scope:
- Extend
compileCompositeLitincl/expr.gofor map type inference - Add logic to choose between Go map, Python Dict, JS Map
- Handle key-value type inference
Deliverables:
- Backend selection for
{"key": value}syntax - Test cases:
m := {"Mon": 1} // map[string]int in Go context import "py/std" m := {"Mon": 1} // *py.Object (Dict) in Python context
Phase 3: Multi-Backend Untyped Primitives (Steps 6-8)
Step 6: Python Backend Type Conversion
Goal: Basic literals can infer to *py.Object
Scope:
- Add
*py.Objectas possible target for untyped int/float/string/bool - Implement conversion logic in
cl/expr.go - Detection based on Python stdlib imports or function signatures
Deliverables:
- Automatic conversion when calling Python functions:
import "py/std" x := 1.2 // deferred std.print(x) // converts to py.Float(1.2)
Step 7: JavaScript Backend Type Conversion
Goal: Basic literals can infer to *js.Object
Scope:
- Similar to Step 6, but for JavaScript backend
- Add
*js.Objectinference paths - Detection based on JavaScript imports
Deliverables:
- JS backend support for untyped literals
- Test cases with JS function calls
Step 8: Multi-Assignment Context Resolution
Goal: Handle variables used in multiple contexts
Scope:
- Implement priority/conflict resolution when variable could be multiple types
- Error handling for ambiguous cases
- Cache resolved types for performance
Deliverables:
- Error messages for ambiguous scenarios:
x := 100 goFunc(x) // wants int pyFunc(x) // wants *py.Object - ERROR: ambiguous
Phase 4: Documentation (Step 9)
Step 9: Documentation Updates
Goal: Document the new untyped literal extension feature
Scope:
- Update
doc/docs.mdwith feature explanation and examples - Update
doc/spec.mdif needed - Add TOC entries
- Provide migration guide for existing code
Deliverables:
- Complete documentation with runnable examples
- Comparison with Go's untyped literals
- Best practices guide
Summary of Approach
Incremental Value:
- Step 1-2: Core infrastructure (enables future steps)
- Step 3: Immediate value (empty slice inference)
- Step 4-5: Major feature (untyped collections)
- Step 6-8: Cross-language integration (Python/JS)
- Step 9: User-facing completion
Workload Balance:
- Small steps: 1-3 (infrastructure, targeted features)
- Medium steps: 4-5, 6-7 (new inference paths)
- Small step: 8-9 (edge cases, docs)
Testing Strategy:
Each step includes test cases in cl/_testgop/ following XGo conventions, ensuring backward compatibility with existing Go code.
Dependencies:
- Steps 1-2 must complete first (foundation)
- Steps 3-8 can be developed in parallel after Step 2
- Step 9 requires all others complete
Would you like me to start with any specific step, or would you prefer adjustments to this breakdown?
@niupilot create a sub-issue for step 1
Sub-issue Created Successfully ✓
I've created a new GitHub issue for Step 1 of the Untyped Literal Extension implementation:
Issue #2464: Step 1: Infrastructure for Deferred Type Variables 🔗 https://github.com/goplus/xgo/issues/2464
Issue Details
Goal: Enable variables to remain untyped until first use
Scope:
- Add deferred type tracking in
cl/compile.go(symbol table extension) - Implement placeholder type for untyped variables (
types.Typ[types.UntypedNil]variant) - Create deferred resolution queue mechanism
- No syntax changes - focus on internal infrastructure
Deliverables:
- New
deferredVarstructure to track unresolved variables - Type resolution deferral mechanism in
blockCtx - Test cases in
cl/_testgop/for simple deferred inference scenarios
Example Test Case:
x := 100 // deferred
x + 200 // resolves to int
The issue has been linked to parent issue #2441 and is ready for implementation.
牛逼~,基于go写了一套go+语言。感觉用法比go要简单易用多了。为啥不彻彻底底通过C重构呀,感觉性能还能提高一层。还能自主版权。收益挺大的