Skip to content

unsafe.Offsetof in generic functions lowers uintptr result as signed int #2251

Description

@cpunion

Reproducer

Tested at xgo-dev/llgo@551626a1de6b6c47a4dcfad7190b074ae9b0d809.

package main

import "unsafe"

type nested[T any] struct {
	first, second T
}

type value[T any] struct {
	first  T
	word   int32
	nested nested[T]
}

func showOffset[T any](item *value[T]) {
	println(unsafe.Offsetof(item.nested))
}

func main() {
	showOffset(&value[bool]{})
}

One way to inspect the generated IR is to build chore/llgen, run it on the reproducer module, and inspect llgo_autogen.ll:

go build -o /tmp/llgen ./chore/llgen
(cd /path/to/reproducer && /tmp/llgen .)
rg 'Print(Int|Uint)' /path/to/reproducer/llgo_autogen.ll

Actual result

The instantiated generic function contains:

call void @"github.com/goplus/llgo/runtime/internal/runtime.PrintInt"(i64 8)

Expected result

unsafe.Offsetof has result type uintptr, so the value should retain the unsigned uintptr LLGo type and println should lower through PrintUint:

call void @"github.com/goplus/llgo/runtime/internal/runtime.PrintUint"(i64 8)

Cause and impact

The generic field-layout fast path in context.offsetOfBuiltinArg currently returns p.prog.Val(int(offset)). That creates a signed int LLSSA expression even though the Go SSA call result is uintptr. The non-generic constant-folded path does not show the problem.

Besides selecting the wrong print helper, losing signedness/type at this boundary can select signed operations for direct consumers of the result. The lowering should construct the constant with p.prog.Uintptr() (as the Sizeof/Alignof lowering already does), and a regression test should assert the returned LLSSA RawType is types.Uintptr.

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