Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ module github.com/gnoverse/gnopls

go 1.25.0

toolchain go1.26.4

require (
github.com/gnolang/gno v0.0.0-20260415100849-375fe89c6d20
github.com/google/go-cmp v0.7.0
Expand Down
28 changes: 23 additions & 5 deletions pkg/gnotypes/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,12 @@ func IsGnoBuiltin(obj types.Object) bool {

switch obj.(type) {
case *types.Func:
// lookup for function only
if obj.Type().(*types.Signature).Recv() != nil {
return false // method
// lookup for function only. A nil/non-signature type means the func
// failed to type-check; treat it as not a builtin (same defensive
// handling as isMethod) rather than panicking on the assertion.
sig, ok := obj.Type().(*types.Signature)
if !ok || sig.Recv() != nil {
return false // unresolved signature or method
}

case *types.TypeName:
Expand Down Expand Up @@ -476,14 +479,21 @@ func init() {
for name, obj := range gnoBuiltin {
switch o := obj.(type) {
case *types.Func:
// Skip funcs whose signature failed to type-check (nil Type).
// See isMethod for why this can happen.
origSig, ok := o.Type().(*types.Signature)
if !ok {
log.Printf("builtin func %q skipped: signature did not type-check", o.Name())
continue
}
// Clone the signature with the shared ctx so any custom builtin
// named types it references resolve to the same package-less
// instances registered in the Universe below — not the
// package-qualified originals from the parsed builtin file.
// Without this, cross(cur) fails to type-check: cross's param
// would be builtin.realm while the Universe realm is the cloned,
// package-less type (identity mismatch).
sig := ctx.CloneTypeWithNilPackage(o.Type()).(*types.Signature)
sig := ctx.CloneTypeWithNilPackage(origSig).(*types.Signature)
newFn := types.NewFunc(token.NoPos, nil, name, sig) // a builtin don't have a pos
types.Universe.Insert(newFn) // register func
log.Printf("builtin func %q has been registered", o.Name())
Expand All @@ -510,6 +520,14 @@ func isMethod(obj types.Object) bool {
if !ok {
return false
}
// A func whose signature failed to type-check has a nil Type (e.g. when a
// recursive-type error elsewhere in the builtin file aborts resolution).
// Treat it as a non-method so callers can filter it out instead of
// panicking on the type assertion below.
sig, ok := fn.Type().(*types.Signature)
if !ok {
return false
}
// Check if the function has a receiver (i.e., is a method)
return fn.Type().(*types.Signature).Recv() != nil
return sig.Recv() != nil
}
85 changes: 14 additions & 71 deletions pkg/gnotypes/builtin/builtin.gno
Original file line number Diff line number Diff line change
Expand Up @@ -13,78 +13,23 @@ import "cmp"
for the language's special identifiers.
*/

// bool is the set of boolean values, true and false.
type bool bool
// NOTE: The predeclared scalar types (bool, the sized ints/uints, floats,
// complex, string, int, uint, uintptr) and the `comparable` constraint are
// intentionally NOT redeclared here. Go's own builtin.go documents them with
// self-referential stubs such as `type bool bool`, but go/types rejects those
// as an "invalid recursive type" error. On Go 1.26+ that error aborts type
// resolution for the whole file, leaving every builtin func (including gno's
// cross/revive/istypednil) with a nil signature — which crashed gnopls at
// init. These identifiers are already provided by types.Universe and are
// skipped during registration regardless, so the stubs served no purpose here
// beyond documentation.

// true and false are the two untyped boolean values.
const (
true = 0 == 0 // Untyped bool.
false = 0 != 0 // Untyped bool.
)

// uint8 is the set of all unsigned 8-bit integers.
// Range: 0 through 255.
type uint8 uint8

// uint16 is the set of all unsigned 16-bit integers.
// Range: 0 through 65535.
type uint16 uint16

// uint32 is the set of all unsigned 32-bit integers.
// Range: 0 through 4294967295.
type uint32 uint32

// uint64 is the set of all unsigned 64-bit integers.
// Range: 0 through 18446744073709551615.
type uint64 uint64

// int8 is the set of all signed 8-bit integers.
// Range: -128 through 127.
type int8 int8

// int16 is the set of all signed 16-bit integers.
// Range: -32768 through 32767.
type int16 int16

// int32 is the set of all signed 32-bit integers.
// Range: -2147483648 through 2147483647.
type int32 int32

// int64 is the set of all signed 64-bit integers.
// Range: -9223372036854775808 through 9223372036854775807.
type int64 int64

// float32 is the set of all IEEE 754 32-bit floating-point numbers.
type float32 float32

// float64 is the set of all IEEE 754 64-bit floating-point numbers.
type float64 float64

// complex64 is the set of all complex numbers with float32 real and
// imaginary parts.
type complex64 complex64

// complex128 is the set of all complex numbers with float64 real and
// imaginary parts.
type complex128 complex128

// string is the set of all strings of 8-bit bytes, conventionally but not
// necessarily representing UTF-8-encoded text. A string may be empty, but
// not nil. Values of string type are immutable.
type string string

// int is a signed integer type that is at least 32 bits in size. It is a
// distinct type, however, and not an alias for, say, int32.
type int int

// uint is an unsigned integer type that is at least 32 bits in size. It is a
// distinct type, however, and not an alias for, say, uint32.
type uint uint

// uintptr is an integer type that is large enough to hold the bit pattern of
// any pointer.
type uintptr uintptr

// byte is an alias for uint8 and is equivalent to uint8 in all ways. It is
// used, by convention, to distinguish byte values from 8-bit unsigned
// integer values.
Expand All @@ -97,12 +42,10 @@ type rune = int32
// any is an alias for interface{} and is equivalent to interface{} in all ways.
type any = interface{}

// comparable is an interface that is implemented by all comparable types
// (booleans, numbers, strings, pointers, channels, arrays of comparable types,
// structs whose fields are all comparable types).
// The comparable interface may only be used as a type parameter constraint,
// not as the type of a variable.
type comparable interface{ comparable }
// comparable is intentionally omitted here; see the note above. It is
// provided by types.Universe and its self-referential documentation stub
// (`type comparable interface{ comparable }`) triggers the same recursive-type
// error.

// iota is a predeclared identifier representing the untyped integer ordinal
// number of the current const specification in a (usually parenthesized)
Expand Down
Loading