diff --git a/rlp/rlpgen/gen.go b/rlp/rlpgen/gen.go index 7ec38a4c38f..c0492b4de93 100644 --- a/rlp/rlpgen/gen.go +++ b/rlp/rlpgen/gen.go @@ -687,6 +687,12 @@ func (bctx *buildContext) makeOp(name *types.Named, typ types.Type, tags rlpstru if bctx.isDecoder(typ) { return nil, fmt.Errorf("type %v implements rlp.Decoder with non-pointer receiver", typ) } + if hasBasicUnderlying(typ) { + // libevm: named types are reduced to their underlying basic type in this loop. + // We're handling named types here by passing the named type as the main type. + // See [named.libevm.go] for more details. + return bctx.makeNamedBasicOp(typ) + } // TODO: same check for encoder? return bctx.makeOp(typ, typ.Underlying(), tags) case *types.Pointer: diff --git a/rlp/rlpgen/gen_test.go b/rlp/rlpgen/gen_test.go index be439902610..69e3fe80771 100644 --- a/rlp/rlpgen/gen_test.go +++ b/rlp/rlpgen/gen_test.go @@ -47,7 +47,7 @@ func init() { } } -var tests = []string{"uints", "nil", "rawvalue", "optional", "bigint", "uint256", "alias"} +var tests = []string{"uints", "nil", "rawvalue", "optional", "bigint", "uint256", "alias", "named.libevm"} func TestOutput(t *testing.T) { for _, test := range tests { diff --git a/rlp/rlpgen/named.libevm.go b/rlp/rlpgen/named.libevm.go new file mode 100644 index 00000000000..7f5aad1e8b8 --- /dev/null +++ b/rlp/rlpgen/named.libevm.go @@ -0,0 +1,55 @@ +// Copyright 2025 the libevm authors. +// +// The libevm additions to go-ethereum are free software: you can redistribute +// them and/or modify them under the terms of the GNU Lesser General Public License +// as published by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// The libevm additions are distributed in the hope that they will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +// General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see +// . + +package main + +import ( + "fmt" + "go/types" +) + +// makeNamedBasicOp is a convenience wrapper for basicOp. +// It returns a basicOp with the named type as the main type instead of the underlying basic type. +func (bctx *buildContext) makeNamedBasicOp(named *types.Named) (op, error) { + underlying := named.Underlying() + basic, ok := underlying.(*types.Basic) + if !ok { + return nil, fmt.Errorf("expected basic type, got %T", underlying) + } + + // We use basic op because it actually supports necessary conversions (through writeNeedsConversion and decodeNeedsConversion) + // for named types. + // The only problem with that is it does not support the named type as the main type. + // So we use the named type as the main type instead of the underlying basic type. + baseOp, err := bctx.makeBasicOp(basic) + if err != nil { + return nil, err + } + + op, ok := baseOp.(basicOp) + if !ok { + return nil, fmt.Errorf("expected basicOp, got %T", baseOp) + } + op.typ = named + + return op, nil +} + +// hasBasicUnderlying checks whether `named` has an underlying basic type. +func hasBasicUnderlying(named *types.Named) bool { + _, ok := named.Underlying().(*types.Basic) + return ok +} diff --git a/rlp/rlpgen/testdata/named.libevm.in.txt b/rlp/rlpgen/testdata/named.libevm.in.txt new file mode 100644 index 00000000000..654acd78bbd --- /dev/null +++ b/rlp/rlpgen/testdata/named.libevm.in.txt @@ -0,0 +1,15 @@ +// -*- mode: go -*- + +package test + +type ( + BoolT bool + Uint64T uint64 + StringT string +) + +type Test struct { + BoolNewT BoolT + Uint64NewT Uint64T + StringNewT StringT +} diff --git a/rlp/rlpgen/testdata/named.libevm.out.txt b/rlp/rlpgen/testdata/named.libevm.out.txt new file mode 100644 index 00000000000..c80f98b0591 --- /dev/null +++ b/rlp/rlpgen/testdata/named.libevm.out.txt @@ -0,0 +1,49 @@ +package test + +import "github.com/ava-labs/libevm/rlp" +import "io" + +func (obj *Test) EncodeRLP(_w io.Writer) error { + w := rlp.NewEncoderBuffer(_w) + _tmp0 := w.List() + w.WriteBool(bool(obj.BoolNewT)) + w.WriteUint64(uint64(obj.Uint64NewT)) + w.WriteString(string(obj.StringNewT)) + w.ListEnd(_tmp0) + return w.Flush() +} + +func (obj *Test) DecodeRLP(dec *rlp.Stream) error { + var _tmp0 Test + { + if _, err := dec.List(); err != nil { + return err + } + // BoolNewT: + _tmp1, err := dec.Bool() + if err != nil { + return err + } + _tmp2 := BoolT(_tmp1) + _tmp0.BoolNewT = _tmp2 + // Uint64NewT: + _tmp3, err := dec.Uint64() + if err != nil { + return err + } + _tmp4 := Uint64T(_tmp3) + _tmp0.Uint64NewT = _tmp4 + // StringNewT: + _tmp5, err := dec.String() + if err != nil { + return err + } + _tmp6 := StringT(_tmp5) + _tmp0.StringNewT = _tmp6 + if err := dec.ListEnd(); err != nil { + return err + } + } + *obj = _tmp0 + return nil +}