[
https://issues.apache.org/jira/browse/THRIFT-6197?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Sylwester Lachiewicz updated THRIFT-6197:
-----------------------------------------
Description:
The Go generator does not resolve typedefs to their underlying definition when
it emits field types, so any typedef of a struct, and any typedef used before
it is declared, produces Go code that does not compile.
Verified with the released 0.24.0 compiler; the generated packages were built
with Go 1.27 against the current lib/go/thrift.
h3. Case 1: typedef declared after first use
{code}
struct Bar {
1: optional Foo foo,
}
typedef i32 Foo
{code}
The field takes the underlying type rather than the alias, and the read path
then assigns a *Foo to it:
{code}
type Foo int32
type Bar struct {
Foo *int32 `thrift:"foo,1" db:"foo" json:"foo,omitempty"`
}
{code}
{noformat}
t5601.go:108:11: cannot use &temp (value of type *Foo) as *int32 value in
assignment
{noformat}
This is THRIFT-5601, and the field-type half of it is THRIFT-5489.
h3. Case 2: typedef of a struct in the same file
{code}
struct Inner { 1: required string s }
typedef Inner InnerAlias
struct Outer { 1: optional InnerAlias a }
service Svc { InnerAlias get(1: InnerAlias x) }
{code}
The alias is generated as a pointer type, and the field then adds a second
pointer:
{code}
type InnerAlias *Inner
...
A *InnerAlias `thrift:"a,1" db:"a" json:"a,omitempty"`
{code}
{noformat}
t4901.go:247:8: cannot use &Inner{} (value of type *Inner) as *InnerAlias
value in assignment
t4901.go:248:16: p.A.Read undefined (type *InnerAlias has no field or method
Read)
t4901.go:282:17: p.A.Write undefined (type *InnerAlias has no field or method
Write)
t4901.go:298:10: p.A.Equals undefined (type *InnerAlias has no field or method
Equals)
t4901.go:472:40: cannot use args.X (variable of type *InnerAlias) as InnerAlias
value in argument to p.handler.Get
{noformat}
This is THRIFT-4901, and the service-signature errors are THRIFT-3491.
h3. Case 3: typedef of a struct from an included file
{code}
// Base.thrift
namespace go a.X.c
struct Foo { 1: required string s }
// Child.thrift
namespace go a.Y.c
include "Base.thrift"
typedef Base.Foo Foo
struct Bar { 1: Foo f }
{code}
{noformat}
Child.go:107:8: cannot use &c.Foo{} (value of type *"a/X/c".Foo) as *Foo value
in assignment
Child.go:108:16: p.F.Read undefined (type *Foo has no field or method Read)
Child.go:141:16: p.F.Write undefined (type *Foo has no field or method Write)
{noformat}
This is THRIFT-3037, open since 2015.
h3. Why one ticket
All three come from the same place: the generator decides a field's Go type
from the typedef itself instead of from the type it resolves to, and separately
emits a pointer alias for a struct typedef. THRIFT-5601, THRIFT-5489,
THRIFT-4901, THRIFT-3491 and THRIFT-3037 are five views of that.
A fix should cover, at minimum: a typedef declared after use, a typedef of a
struct in the same file, a typedef of a struct across an include, and a typedef
of a container. The THRIFT-5601 fix was reverted once by THRIFT-5685, so the
regression coverage matters as much as the fix: a struct declared after its
first use has to keep a pointer getter, or bar.GetBar().GetFoo() stops
compiling.
h3. Scope of a fix
Generating a struct typedef as a Go alias (type Alias = Inner) rather than a
defined type only changes output that does not compile today, so it carries
almost nothing working with it. Measured on PR #3812 by regenerating every IDL
under test/, lib/go/test/ and tutorial/ with the released generator and with
the patched one: 3 of 325 generated files differ, and every changed line is a
typedef-of-struct declaration.
The one place working code does change is a typedef of a struct that the IDL
declares but never uses. That compiles today, so hand-written Go can hold such
a value, and the pointer moves from inside the alias to outside it. That is
worth a release note.
was:
The Go generator does not resolve typedefs to their underlying definition when
it emits field types, so any typedef of a struct, and any typedef used before
it is declared, produces Go code that does not compile.
Verified with the released 0.24.0 compiler; the generated packages were built
with Go 1.27 against the current lib/go/thrift.
h3. Case 1: typedef declared after first use
{code}
struct Bar {
1: optional Foo foo,
}
typedef i32 Foo
{code}
The field takes the underlying type rather than the alias, and the read path
then assigns a *Foo to it:
{code}
type Foo int32
type Bar struct {
Foo *int32 `thrift:"foo,1" db:"foo" json:"foo,omitempty"`
}
{code}
{noformat}
t5601.go:108:11: cannot use &temp (value of type *Foo) as *int32 value in
assignment
{noformat}
This is THRIFT-5601, and the field-type half of it is THRIFT-5489.
h3. Case 2: typedef of a struct in the same file
{code}
struct Inner { 1: required string s }
typedef Inner InnerAlias
struct Outer { 1: optional InnerAlias a }
service Svc { InnerAlias get(1: InnerAlias x) }
{code}
The alias is generated as a pointer type, and the field then adds a second
pointer:
{code}
type InnerAlias *Inner
...
A *InnerAlias `thrift:"a,1" db:"a" json:"a,omitempty"`
{code}
{noformat}
t4901.go:247:8: cannot use &Inner{} (value of type *Inner) as *InnerAlias
value in assignment
t4901.go:248:16: p.A.Read undefined (type *InnerAlias has no field or method
Read)
t4901.go:282:17: p.A.Write undefined (type *InnerAlias has no field or method
Write)
t4901.go:298:10: p.A.Equals undefined (type *InnerAlias has no field or method
Equals)
t4901.go:472:40: cannot use args.X (variable of type *InnerAlias) as InnerAlias
value in argument to p.handler.Get
{noformat}
This is THRIFT-4901, and the service-signature errors are THRIFT-3491.
h3. Case 3: typedef of a struct from an included file
{code}
// Base.thrift
namespace go a.X.c
struct Foo { 1: required string s }
// Child.thrift
namespace go a.Y.c
include "Base.thrift"
typedef Base.Foo Foo
struct Bar { 1: Foo f }
{code}
{noformat}
Child.go:107:8: cannot use &c.Foo{} (value of type *"a/X/c".Foo) as *Foo value
in assignment
Child.go:108:16: p.F.Read undefined (type *Foo has no field or method Read)
Child.go:141:16: p.F.Write undefined (type *Foo has no field or method Write)
{noformat}
This is THRIFT-3037, open since 2015.
h3. Why one ticket
All three come from the same place: the generator decides a field's Go type
from the typedef itself instead of from the type it resolves to, and separately
emits a pointer alias for a struct typedef. THRIFT-5601, THRIFT-5489,
THRIFT-4901, THRIFT-3491 and THRIFT-3037 are five views of that. THRIFT-5463 is
likely the same, though it was not reproduced here.
A fix should cover, at minimum: a typedef declared after use, a typedef of a
struct in the same file, a typedef of a struct across an include, and a typedef
of a container. Note that the THRIFT-5601 fix was reverted once before, so the
regression coverage matters as much as the fix.
> Go generator mishandles typedefs, so aliased structs and forward-declared
> typedefs generate code that does not compile
> ----------------------------------------------------------------------------------------------------------------------
>
> Key: THRIFT-6197
> URL: https://issues.apache.org/jira/browse/THRIFT-6197
> Project: Thrift
> Issue Type: Bug
> Components: Go - Compiler
> Reporter: Sylwester Lachiewicz
> Priority: Major
> Time Spent: 10m
> Remaining Estimate: 0h
>
> The Go generator does not resolve typedefs to their underlying definition
> when it emits field types, so any typedef of a struct, and any typedef used
> before it is declared, produces Go code that does not compile.
> Verified with the released 0.24.0 compiler; the generated packages were built
> with Go 1.27 against the current lib/go/thrift.
> h3. Case 1: typedef declared after first use
> {code}
> struct Bar {
> 1: optional Foo foo,
> }
> typedef i32 Foo
> {code}
> The field takes the underlying type rather than the alias, and the read path
> then assigns a *Foo to it:
> {code}
> type Foo int32
> type Bar struct {
> Foo *int32 `thrift:"foo,1" db:"foo" json:"foo,omitempty"`
> }
> {code}
> {noformat}
> t5601.go:108:11: cannot use &temp (value of type *Foo) as *int32 value in
> assignment
> {noformat}
> This is THRIFT-5601, and the field-type half of it is THRIFT-5489.
> h3. Case 2: typedef of a struct in the same file
> {code}
> struct Inner { 1: required string s }
> typedef Inner InnerAlias
> struct Outer { 1: optional InnerAlias a }
> service Svc { InnerAlias get(1: InnerAlias x) }
> {code}
> The alias is generated as a pointer type, and the field then adds a second
> pointer:
> {code}
> type InnerAlias *Inner
> ...
> A *InnerAlias `thrift:"a,1" db:"a" json:"a,omitempty"`
> {code}
> {noformat}
> t4901.go:247:8: cannot use &Inner{} (value of type *Inner) as *InnerAlias
> value in assignment
> t4901.go:248:16: p.A.Read undefined (type *InnerAlias has no field or method
> Read)
> t4901.go:282:17: p.A.Write undefined (type *InnerAlias has no field or method
> Write)
> t4901.go:298:10: p.A.Equals undefined (type *InnerAlias has no field or
> method Equals)
> t4901.go:472:40: cannot use args.X (variable of type *InnerAlias) as
> InnerAlias value in argument to p.handler.Get
> {noformat}
> This is THRIFT-4901, and the service-signature errors are THRIFT-3491.
> h3. Case 3: typedef of a struct from an included file
> {code}
> // Base.thrift
> namespace go a.X.c
> struct Foo { 1: required string s }
> // Child.thrift
> namespace go a.Y.c
> include "Base.thrift"
> typedef Base.Foo Foo
> struct Bar { 1: Foo f }
> {code}
> {noformat}
> Child.go:107:8: cannot use &c.Foo{} (value of type *"a/X/c".Foo) as *Foo
> value in assignment
> Child.go:108:16: p.F.Read undefined (type *Foo has no field or method Read)
> Child.go:141:16: p.F.Write undefined (type *Foo has no field or method Write)
> {noformat}
> This is THRIFT-3037, open since 2015.
> h3. Why one ticket
> All three come from the same place: the generator decides a field's Go type
> from the typedef itself instead of from the type it resolves to, and
> separately emits a pointer alias for a struct typedef. THRIFT-5601,
> THRIFT-5489, THRIFT-4901, THRIFT-3491 and THRIFT-3037 are five views of that.
> A fix should cover, at minimum: a typedef declared after use, a typedef of a
> struct in the same file, a typedef of a struct across an include, and a
> typedef of a container. The THRIFT-5601 fix was reverted once by THRIFT-5685,
> so the regression coverage matters as much as the fix: a struct declared
> after its first use has to keep a pointer getter, or bar.GetBar().GetFoo()
> stops compiling.
> h3. Scope of a fix
> Generating a struct typedef as a Go alias (type Alias = Inner) rather than a
> defined type only changes output that does not compile today, so it carries
> almost nothing working with it. Measured on PR #3812 by regenerating every
> IDL under test/, lib/go/test/ and tutorial/ with the released generator and
> with the patched one: 3 of 325 generated files differ, and every changed line
> is a typedef-of-struct declaration.
> The one place working code does change is a typedef of a struct that the IDL
> declares but never uses. That compiles today, so hand-written Go can hold
> such a value, and the pointer moves from inside the alias to outside it. That
> is worth a release note.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)