Skip to content
Merged
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
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Performance
- **`Transpile`** recovers most of the v1.17.0 correctness cost: the AST walks
follow cached per-type reflection plans that skip fields which cannot hold a
rewrite target, and cheap source checks skip the `||`, string-literal and
identifier-requoting passes when the SQL cannot need them. A short MySQL
`CREATE TABLE` to PostgreSQL goes from 5.2 µs to 2.7 µs (52 to 43
allocations); wide `OR`/`AND` chains are about 68% faster and `||` chains
about 55% faster. Output and errors are unchanged on 955,476 differential
cases. (#889)

## [1.17.0] - 2026-10-29 — Real-World Dialect Corpus, Dialect-Faithful Round Trips, Security Scanner & LSP Hardening

Covers everything merged since v1.16.0, mostly the October 2026 dialect audit:
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ metrics → (no deps)
keywords → (no deps)
token → (no deps)
tokenizer → models, errors, metrics, keywords
ast → models, metrics
ast → models, metrics, internal/reflplan
parser → models, errors, keywords, token, tokenizer, ast

# Higher-level / product packages
Expand Down
125 changes: 125 additions & 0 deletions pkg/internal/reflplan/reflplan.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Package reflplan precomputes, per reflect.Type, which parts of a value can
// hold an interface value. Reflective AST walkers use it to skip strings,
// numbers, positions and other leaf data without calling reflect.Type.Field
// on every visit. Plans are immutable once published and safe for concurrent
// use. Reach is the least fixpoint over the reachable type graph, seeded by
// interface kinds. Only exported struct fields participate; Fields contains
// exactly those that reach an interface, in declaration order. Pointer, slice,
// array (including zero-length arrays) and map plans always have Elem set.
// Plans are fully built before LoadOrStore publishes them and never mutated
// afterwards. Walkers prune only interface-free static types: every rewrite
// site is an interface slot, and BinaryExpression, UnaryExpression, FunctionCall,
// ParenthesizedExpression and LiteralValue all reach one. Reachable slots keep
// their original visit order.
package reflplan

import (
"reflect"
"sync"
)

// Plan describes how to traverse values of one type.
type Plan struct {
Type reflect.Type
Kind reflect.Kind
// Reach reports whether a value of this type can (transitively, through
// exported fields, elements, pointers or interfaces) hold an interface.
Reach bool
// Fields lists the exported struct fields whose type Reaches, in
// declaration order.
Fields []Field
// Elem is the element plan of a pointer, slice, array or map.
Elem *Plan
}

// Field is one exported struct field worth visiting.
type Field struct {
Index int
Plan *Plan
}

var cache sync.Map // reflect.Type -> *Plan

// Of returns the plan for t, building it (and the plans of every type
// reachable from t) on first use.
func Of(t reflect.Type) *Plan {
if p, ok := cache.Load(t); ok {
if plan, ok := p.(*Plan); ok {
return plan
}
}
return build(t)
}

type rawField struct {
index int
t reflect.Type
}

func build(root reflect.Type) *Plan {
local := map[reflect.Type]*Plan{}
raw := map[*Plan][]rawField{}
var order []*Plan
var get func(t reflect.Type) *Plan
get = func(t reflect.Type) *Plan {
if p, ok := local[t]; ok {
return p
}
if p, ok := cache.Load(t); ok {
if plan, ok := p.(*Plan); ok {
local[t] = plan
return plan
}
}
p := &Plan{Type: t, Kind: t.Kind(), Reach: t.Kind() == reflect.Interface}
local[t] = p
order = append(order, p)
switch t.Kind() {
case reflect.Pointer, reflect.Slice, reflect.Array, reflect.Map:
p.Elem = get(t.Elem())
case reflect.Struct:
var fs []rawField
for i := 0; i < t.NumField(); i++ {
if sf := t.Field(i); sf.IsExported() {
fs = append(fs, rawField{i, sf.Type})
get(sf.Type)
}
}
raw[p] = fs
}
return p
}
rootPlan := get(root)
// Least fixpoint of Reach over the (possibly cyclic) type graph.
for changed := true; changed; {
changed = false
for _, p := range order {
if p.Reach {
continue
}
switch p.Kind {
case reflect.Pointer, reflect.Slice, reflect.Array, reflect.Map:
p.Reach = p.Elem.Reach
case reflect.Struct:
for _, f := range raw[p] {
if local[f.t].Reach {
p.Reach = true
break
}
}
}
changed = changed || p.Reach
}
}
for _, p := range order {
for _, f := range raw[p] {
if fp := local[f.t]; fp.Reach {
p.Fields = append(p.Fields, Field{Index: f.index, Plan: fp})
}
}
}
for _, p := range order {
cache.LoadOrStore(p.Type, p)
}
return rootPlan
}
135 changes: 135 additions & 0 deletions pkg/internal/reflplan/reflplan_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package reflplan

import (
"reflect"
"sync"
"testing"
)

type leaf struct {
A string
B int
C struct{ X, Y int }
}
type selfLeaf struct {
Name string
Next *selfLeaf
}
type withIface struct {
Name string
E any
L leaf
S []leaf
M map[string]any
P *selfLeaf
//lint:ignore U1000 an unexported interface field must be excluded from Fields
hid any
}
type cycA struct {
B *cycB
N int
}
type cycB struct {
A []cycA
Val any
}

type Embedded struct{ Value any }

func TestPlanContainers(t *testing.T) {
for _, value := range []any{map[string]any{}, []any{}, [0]any{}, (*any)(nil)} {
typ := reflect.TypeOf(value)
p := Of(typ)
if !p.Reach || p.Elem == nil || !p.Elem.Reach || p.Elem.Type != typ.Elem() {
t.Errorf("%v: incomplete container plan: %+v", typ, p)
}
}
// Only map values carry rewrite slots; interface keys do not reach.
for _, value := range []any{map[any]int{}, []leaf{}, [0]leaf{}, (*selfLeaf)(nil)} {
typ := reflect.TypeOf(value)
p := Of(typ)
if p.Reach || p.Elem == nil || p.Elem.Reach || p.Elem.Type != typ.Elem() {
t.Errorf("%v: invalid leaf container plan: %+v", typ, p)
}
}
typ := reflect.TypeOf(struct {
Before int
Embedded
After string
}{})
p := Of(typ)
if !p.Reach || !reflect.DeepEqual(indices(p), []int{1}) || !reflect.DeepEqual(indices(p.Fields[0].Plan), []int{0}) {
t.Fatalf("embedded plan = %+v", p)
}
if Of(reflect.TypeOf(struct{ hidden any }{})).Reach {
t.Fatal("unexported interface field must not reach")
}
}

func indices(p *Plan) []int {
var out []int
for _, f := range p.Fields {
out = append(out, f.Index)
}
return out
}

func TestPlans(t *testing.T) {
if Of(reflect.TypeOf(leaf{})).Reach || Of(reflect.TypeOf(selfLeaf{})).Reach {
t.Fatal("interface-free types must not reach")
}
p := Of(reflect.TypeOf(withIface{}))
if !p.Reach || !reflect.DeepEqual(indices(p), []int{1, 4}) {
t.Fatalf("withIface fields = %v", indices(p))
}
a := Of(reflect.TypeOf(&cycA{}))
if !a.Reach || !a.Elem.Reach || !reflect.DeepEqual(indices(a.Elem), []int{0}) {
t.Fatalf("cycA = %+v", a.Elem)
}
b := a.Elem.Fields[0].Plan
if b.Elem == nil || !b.Reach || !reflect.DeepEqual(indices(b.Elem), []int{0, 1}) {
t.Fatalf("cycB = %+v", b)
}
back := b.Elem.Fields[0].Plan
if back.Elem == nil || back.Elem.Type != reflect.TypeOf(cycA{}) || !back.Reach {
t.Fatalf("cycle back edge = %+v", back)
}
}

func TestConcurrentBuild(t *testing.T) {
// Unique types exercise cold-cache publication even after TestPlans.
type concurrentA struct {
Next *concurrentA
Value any
}
typ := reflect.TypeOf(struct{ A []map[int]*concurrentA }{})
start := make(chan struct{})
var wg sync.WaitGroup
for i := 0; i < 32; i++ {
wg.Add(1)
go func() {
defer wg.Done()
<-start
for range 32 {
p := Of(typ)
if !p.Reach || !reflect.DeepEqual(indices(p), []int{0}) {
t.Errorf("incomplete published plan: %+v", p)
return
}
elem := p.Fields[0].Plan
for _, kind := range []reflect.Kind{reflect.Slice, reflect.Map, reflect.Pointer} {
if elem.Kind != kind || elem.Elem == nil || !elem.Reach {
t.Errorf("incomplete container plan: %+v", elem)
return
}
elem = elem.Elem
}
if elem.Kind != reflect.Struct || !elem.Reach || !reflect.DeepEqual(indices(elem), []int{0, 1}) {
t.Errorf("incomplete cycle plan: %+v", elem)
}
}
}()
}
close(start)
wg.Wait()
}
Loading
Loading