Skip to content

frontend/backend: redundant int(...) construction over already-int values is not compile-ready #46

Description

@Iridium-Zero

Summary

Compiled scripts currently do not have a clear, stable contract for redundant same-type int(...) construction.

A shape as simple as int(value) where value is already statically int should behave like a trivial identity / normalization step, but the current pipeline does not define an obvious compile-ready route for that case.

Earlier investigation briefly misframed this as a c_int_to_int or int(float) issue. After narrowing the source shape, the real unresolved surface is repeated construction of already-int values.

Background

This surfaced during benchmark work while restoring and adjusting container-heavy benchmark scripts.

To keep compilation moving, the offending redundant int(...) shape had to be removed instead of relying on it as a harmless source-level normalization.

Although the original investigation started near quadtree benchmark work, this is not a quadtree-specific problem. It is a broader compile-surface contract gap around repeated builtin int construction.

Observed Behavior

A redundant source form like this is not clearly treated as a compile-surface no-op:

func keep(value: int) -> int:
    return int(value)

From code inspection, the current pipeline does not model this as a dedicated identity path. Instead, bare int(...) is handled as builtin constructor resolution, which means the result depends on constructor metadata and boundary compatibility rules rather than a simple same-type roundtrip rule.

The practical consequence is that source authors cannot safely assume that redundant int(...) around already-int values is compile-ready, even though the source-level intent is trivial.

Minimal Reproduction

class_name RedundantIntConstructor
extends Node

func keep(value: int) -> int:
    return int(value)

This issue is about the redundant same-type constructor itself. It is intentionally narrower than unrelated numeric routes such as int(float_value).

Cause Chain

  1. Frontend expression typing treats bare int(...) as a builtin type-meta constructor call, not as an identity operation.

    • FrontendExpressionSemanticSupport.resolveCallExpressionType(...)
    • FrontendExpressionSemanticSupport.resolveBareTypeMetaConstructorCallExpression(...)
  2. Builtin constructor applicability is then decided through constructor metadata and callable-argument matching.

    • FrontendConstructorResolutionSupport.resolveBuiltinConstructor(...)
    • FrontendConstructorResolutionSupport.matchesCallableArguments(...)
  3. The boundary-compatibility layer defines special routes such as ALLOW_WITH_PACK and ALLOW_WITH_INTRINSIC_CAST, but it does not expose a dedicated redundant same-type int -> int identity contract.

    • FrontendVariantBoundaryCompatibility.classify(...)
  4. Non-Variant builtin constructor calls lower through the ordinary constructor path rather than a no-op/self-roundtrip path.

    • FrontendSequenceItemInsnLoweringProcessors.lowerConstructorCall(...)
  5. Backend regular builtin construction expects exact constructor metadata or a supported helper-backed constructor surface.

    • CBuiltinBuilder.constructRegularBuiltin(...)
  6. The generated builtin constructor surface exposes entries such as:

    • godot_new_int_with_Variant
    • godot_new_int_with_String
  7. There is no obvious corresponding godot_new_int_with_int self-constructor surface in the generated builtin header, so redundant int(int_value) does not have a clearly defined backend-ready route.

  8. The result is a compile-surface gap: repeated int(...) over an already-int value is not modeled as a guaranteed safe identity operation, and benchmark/script work has to remove that source shape instead of relying on it.

Expected Behavior

GDCC should make the contract for redundant same-type int(...) explicit.

The most natural behavior is:

  • int(value) where value is already statically int should compile successfully.
  • The route should lower either as a no-op / slot reuse or as another clearly defined backend-safe identity form.

If the project intentionally does not want to support redundant builtin self-construction, then the compiler should reject it at one clear frontend boundary with a targeted diagnostic.

What should not happen is leaving the source form in a compile-blocking gray area where it is semantically trivial but not actually compile-ready.

Scope

This issue is about redundant same-type int(...) construction in compiled scripts.

It is not primarily:

  • a c_int_to_int intrinsic generation issue,
  • a c_int_to_float issue,
  • the broader int(float_value) route,
  • a quadtree algorithm bug,
  • the existing int(Variant) special constructor path.

The benchmark work only exposed the gap; it is not the root cause.

Relevant Implementation Points

  • src/main/java/gd/script/gdcc/frontend/sema/analyzer/support/FrontendExpressionSemanticSupport.java
    • resolveCallExpressionType(...)
    • resolveBareTypeMetaConstructorCallExpression(...)
  • src/main/java/gd/script/gdcc/frontend/sema/analyzer/support/FrontendConstructorResolutionSupport.java
    • resolveBuiltinConstructor(...)
    • matchesCallableArguments(...)
  • src/main/java/gd/script/gdcc/frontend/sema/analyzer/support/FrontendVariantBoundaryCompatibility.java
  • src/main/java/gd/script/gdcc/frontend/lowering/pass/body/FrontendSequenceItemInsnLoweringProcessors.java
    • lowerConstructorCall(...)
  • src/main/java/gd/script/gdcc/backend/c/gen/CBuiltinBuilder.java
    • constructRegularBuiltin(...)
  • src/test/java/gd/script/gdcc/backend/c/gen/CConstructInsnGenTest.java
    • constructBuiltinShouldRejectVariantOperandWithoutExactMetadata()
  • src/main/c/codegen/include_451/godot/godot_builtin.h
    • current godot_new_int_with_* constructor surface
  • doc/module_impl/frontend/frontend_builtin_constructor_variant_argument_plan.md

Suggested Acceptance Coverage

Add focused coverage for redundant same-type int(...):

  • frontend semantic coverage for return int(value) where value: int,
  • lowering coverage proving the accepted form does not depend on a nonexistent self-constructor surface,
  • backend/codegen coverage proving the route either:
    • compiles as an identity/no-op contract, or
    • fails immediately with one intentional, explicit diagnostic,
  • regression coverage from a real compiled script fixture so benchmark and container-heavy scripts can rely on the chosen contract consistently.

Notes

This issue replaces the earlier, narrower misread that focused on c_int_to_int / int(float) during benchmark investigation.

The corrected scope is simpler: repeated int(...) over already-int values should not be an accidental compile blocker.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions