gop icon indicating copy to clipboard operation
gop copied to clipboard

Untyped Literal Extension

Open xushiwei opened this issue 2 months ago • 5 comments

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 be float64, float32, or their named types.
  • untyped complex (e.g., 1 + 2i), which could actually be complex128, complex64, or their named types.
  • untyped string (e.g., "Hello"), which could actually be string or its named type.
  • untyped rune (e.g., 'a'), which could actually be rune or its named type.
  • untyped bool (e.g., true), which could actually be bool or its named type.
  • untyped nil, which could actually be any pointer type, including unsafe.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)
}

xushiwei avatar Oct 05 '25 23:10 xushiwei

@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.

xushiwei avatar Oct 11 '25 07:10 xushiwei

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 deferredVar structure 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 compileAssignStmt in cl/stmt.go to defer type for x := literal patterns
  • 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 compileSliceLit in cl/expr.go to 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)
  • 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 compileCompositeLit in cl/expr.go for 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.Object as 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.Object inference 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.md with feature explanation and examples
  • Update doc/spec.md if 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?


Branch: codeagent/claude/issue-2441-1760167519

niupilot[bot] avatar Oct 11 '25 07:10 niupilot[bot]

@niupilot create a sub-issue for step 1

xushiwei avatar Oct 11 '25 08:10 xushiwei

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 deferredVar structure 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.


Branch: codeagent/claude/issue-2441-1760167519

niupilot[bot] avatar Oct 11 '25 08:10 niupilot[bot]

牛逼~,基于go写了一套go+语言。感觉用法比go要简单易用多了。为啥不彻彻底底通过C重构呀,感觉性能还能提高一层。还能自主版权。收益挺大的

VinchanGit avatar Oct 21 '25 06:10 VinchanGit