From a34c84482f9231c11fdf8f2b1164b72d0f08ec1a Mon Sep 17 00:00:00 2001 From: James Harris Date: Sat, 2 Nov 2024 20:18:55 +1000 Subject: [PATCH] WIP [ci skip] --- .../staticconfig/{varargs.go => _varargs.go} | 4 +- config/staticconfig/internal/ssax/value.go | 89 +++++++++++++++++++ config/staticconfig/route.go | 17 +++- 3 files changed, 105 insertions(+), 5 deletions(-) rename config/staticconfig/{varargs.go => _varargs.go} (96%) diff --git a/config/staticconfig/varargs.go b/config/staticconfig/_varargs.go similarity index 96% rename from config/staticconfig/varargs.go rename to config/staticconfig/_varargs.go index 0bf7a55..ad95121 100644 --- a/config/staticconfig/varargs.go +++ b/config/staticconfig/_varargs.go @@ -48,7 +48,7 @@ func resolveVariadic[ E any, B configbuilder.EntityBuilder[T, E], ]( - b B, + builderXXXXXXXXXXX B, inst ssa.CallInstruction, ) iter.Seq[ssa.Value] { return func(yield func(ssa.Value) bool) { @@ -61,7 +61,7 @@ func resolveVariadic[ array, ok := findAllocation(variadics) if !ok { - b.Partial() + builderXXXXXXXXXXX.Partial() return } diff --git a/config/staticconfig/internal/ssax/value.go b/config/staticconfig/internal/ssax/value.go index 6cdf4cd..2a38f19 100644 --- a/config/staticconfig/internal/ssax/value.go +++ b/config/staticconfig/internal/ssax/value.go @@ -31,6 +31,80 @@ func StaticValue(v ssa.Value) optional.Optional[ssa.Value] { return optional.None[ssa.Value]() } +// StaticValuesFromArguments returns the static value(s) used as arguments in +// the given function call. +func StaticValuesFromArguments( + inst ssa.CallInstruction, +) ( + args []optional.Optional[ssa.Value], + varargs []SliceElem, + exhaustive bool, +) { + call := inst.Common() + sig := call.Signature() + + n := sig.Params().Len() + if sig.Variadic() { + n-- + } + + args = make([]optional.Optional[ssa.Value], n) + + for i, arg := range call.Args[:n] { + args[i] = StaticValue(arg) + } + + if !sig.Variadic() { + return args, varargs, true + } + + varargs, exhaustive = StaticValuesFromSlice(call.Args[n]) + return args, varargs, exhaustive +} + +// SliceElem represents an element within a slice. +type SliceElem struct { + Index optional.Optional[int] + Value optional.Optional[ssa.Value] +} + +// StaticValuesFromSlice returns the static value(s) contained within a slice. +func StaticValuesFromSlice( + v ssa.Value, +) (elems []SliceElem, exhaustive bool) { + array, ok := findUnderlyingArrayAllocation(v) + if !ok { + return nil, false + } + + vinst := v.(ssa.Instruction) + + for b := range WalkBlock(array.Block()) { + if vinst != nil && !PathExists(b, vinst.Block()) { + continue + } + + for inst := range InstructionsBefore(b, vinst) { + switch inst := inst.(type) { + case *ssa.Store: + if addr, ok := inst.Addr.(*ssa.IndexAddr); ok { + if addr.X == array { + elems = append( + elems, + SliceElem{ + Index: AsInt(addr.Index), + Value: StaticValue(inst.Val), + }, + ) + } + } + } + } + } + + return elems, true +} + // staticValuesFromInstruction returns the static value(s) that result from // evaluating the given instruction. // @@ -135,3 +209,18 @@ func equal(a, b ssa.Value) bool { return false } + +// findUnderlyingArrayAllocation returns the underlying array allocation of a +// slice. +func findUnderlyingArrayAllocation(v ssa.Value) (*ssa.Alloc, bool) { + switch v := v.(type) { + case *ssa.Alloc: + return v, true + + case *ssa.Slice: + return findUnderlyingArrayAllocation(v.X) + + default: + return nil, false + } +} diff --git a/config/staticconfig/route.go b/config/staticconfig/route.go index 54d0688..1cca71a 100644 --- a/config/staticconfig/route.go +++ b/config/staticconfig/route.go @@ -3,6 +3,7 @@ package staticconfig import ( "github.com/dogmatiq/enginekit/config" "github.com/dogmatiq/enginekit/config/internal/configbuilder" + "github.com/dogmatiq/enginekit/config/staticconfig/internal/ssax" "github.com/dogmatiq/enginekit/internal/typename" "golang.org/x/tools/go/ssa" ) @@ -14,12 +15,22 @@ func analyzeRoutes[ ]( ctx *configurerCallContext[T, H, B], ) { - for r := range resolveVariadic(ctx.Builder, ctx.Instruction) { + _, varargs, exhaustive := ssax.StaticValuesFromArguments(ctx.Instruction) + if !exhaustive { + ctx.Builder.Partial() + } + + for _, elem := range varargs { ctx.Builder.Route(func(b *configbuilder.RouteBuilder) { - if ctx.IsSpeculative { + if ctx.IsSpeculative || !elem.Index.IsPresent() { b.Speculative() // TODO: is this correct? } - analyzeRoute(ctx.context, b, r) + + if r, ok := elem.Value.TryGet(); ok { + analyzeRoute(ctx.context, b, r) + } else { + b.Partial() + } }) } }