From f4eb54b8354f9a61cea98ffe93cb3208f14eee4a Mon Sep 17 00:00:00 2001 From: James Shubin Date: Sun, 23 May 2021 19:15:45 -0400 Subject: [PATCH] lang: funcs: Add more invariants to contains func This adds even more invariants to contains that I might have missed. It may be redundant, or it may help. It also adds some tests. --- lang/funcs/contains_polyfunc.go | 31 +++++++++++++--- .../TestAstFunc2/contains0.output | 4 ++ .../TestAstFunc2/contains0/main.mcl | 37 +++++++++++++++++++ 3 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 lang/interpret_test/TestAstFunc2/contains0.output create mode 100644 lang/interpret_test/TestAstFunc2/contains0/main.mcl diff --git a/lang/funcs/contains_polyfunc.go b/lang/funcs/contains_polyfunc.go index 112f1727..8e8e889a 100644 --- a/lang/funcs/contains_polyfunc.go +++ b/lang/funcs/contains_polyfunc.go @@ -115,7 +115,7 @@ func (obj *ContainsPolyFunc) Unify(expr interfaces.Expr) ([]interfaces.Invariant invariants = append(invariants, invar) // generator function to link this to the right type - fn := obj.fnBuilder(false, expr, dummyNeedle, dummyHaystack) + fn := obj.fnBuilder(false, expr, dummyNeedle, dummyHaystack, dummyOut) invar = &interfaces.GeneratorInvariant{ Func: fn, } @@ -129,7 +129,7 @@ func (obj *ContainsPolyFunc) Unify(expr interfaces.Expr) ([]interfaces.Invariant // invariant. This can only happen once, because by then we'll have given all // the new information we can, and falsely producing redundant information is a // good way to stall the solver if it thinks it keeps learning more things! -func (obj *ContainsPolyFunc) fnBuilder(recurse bool, expr, dummyNeedle, dummyHaystack interfaces.Expr) func(fnInvariants []interfaces.Invariant, solved map[interfaces.Expr]*types.Type) ([]interfaces.Invariant, error) { +func (obj *ContainsPolyFunc) fnBuilder(recurse bool, expr, dummyNeedle, dummyHaystack, dummyOut interfaces.Expr) func(fnInvariants []interfaces.Invariant, solved map[interfaces.Expr]*types.Type) ([]interfaces.Invariant, error) { return func(fnInvariants []interfaces.Invariant, solved map[interfaces.Expr]*types.Type) ([]interfaces.Invariant, error) { for _, invariant := range fnInvariants { // search for this special type of invariant @@ -141,7 +141,7 @@ func (obj *ContainsPolyFunc) fnBuilder(recurse bool, expr, dummyNeedle, dummyHay if cfavInvar.Func != expr { continue } - // cfavInvar.Expr is the ExprCall! + // cfavInvar.Expr is the ExprCall! (the return pointer) // cfavInvar.Args are the args that ExprCall uses! if l := len(cfavInvar.Args); l != 2 { return nil, fmt.Errorf("unable to build function with %d args", l) @@ -151,6 +151,14 @@ func (obj *ContainsPolyFunc) fnBuilder(recurse bool, expr, dummyNeedle, dummyHay var invar interfaces.Invariant if !recurse { // only do this once! + // add the relationship to the returned value + invar = &interfaces.EqualityInvariant{ + Expr1: dummyOut, + Expr2: cfavInvar.Expr, + } + invariants = append(invariants, invar) + + // add the relationships to the called args invar = &interfaces.EqualityInvariant{ Expr1: dummyNeedle, Expr2: cfavInvar.Args[0], @@ -162,17 +170,28 @@ func (obj *ContainsPolyFunc) fnBuilder(recurse bool, expr, dummyNeedle, dummyHay Expr2: cfavInvar.Args[1], } invariants = append(invariants, invar) + + // TODO: do we return this relationship with ExprCall? + invar = &interfaces.EqualityWrapCallInvariant{ + // TODO: should Expr1 and Expr2 be reversed??? + Expr1: cfavInvar.Expr, + //Expr2Func: cfavInvar.Func, // same as below + Expr2Func: expr, + } + invariants = append(invariants, invar) } var needleTyp *types.Type - if typ, err := cfavInvar.Args[1].Type(); err == nil { + // Instead of using cfavInvar.Args[*].Type() I think we + // can probably rely on the solved to find this for us! + if typ, exists := solved[cfavInvar.Args[1]]; exists { if k := typ.Kind; k == types.KindList { needleTyp = typ.Val // contained element type } } - if typ, err := cfavInvar.Args[0].Type(); err == nil { + if typ, exists := solved[cfavInvar.Args[0]]; exists { if err := needleTyp.Cmp(typ); needleTyp != nil && err != nil { // inconsistent types! return nil, errwrap.Wrapf(err, "inconsistent type") @@ -189,7 +208,7 @@ func (obj *ContainsPolyFunc) fnBuilder(recurse bool, expr, dummyNeedle, dummyHay if needleTyp == nil { // recurse-- we build a new one! - fn := obj.fnBuilder(true, expr, dummyNeedle, dummyHaystack) + fn := obj.fnBuilder(true, expr, dummyNeedle, dummyHaystack, dummyOut) invar = &interfaces.GeneratorInvariant{ Func: fn, } diff --git a/lang/interpret_test/TestAstFunc2/contains0.output b/lang/interpret_test/TestAstFunc2/contains0.output new file mode 100644 index 00000000..fcf9d47f --- /dev/null +++ b/lang/interpret_test/TestAstFunc2/contains0.output @@ -0,0 +1,4 @@ +Vertex: test[passed1] +Vertex: test[passed2] +Vertex: test[passed3] +Vertex: test[passed4] diff --git a/lang/interpret_test/TestAstFunc2/contains0/main.mcl b/lang/interpret_test/TestAstFunc2/contains0/main.mcl new file mode 100644 index 00000000..472cae11 --- /dev/null +++ b/lang/interpret_test/TestAstFunc2/contains0/main.mcl @@ -0,0 +1,37 @@ +$ints = [13, 42, 0, -37,] + +$s1 = if contains(42, $ints) { + "passed1" +} else { + "failed" +} + +$s2 = if contains(99, $ints) { + "failed" +} else { + "passed2" +} + +$intlists = [ + [13, 99,], + [42, 13,], + [0,], + [-37,], +] + +$s3 = if contains([42, 13,], $intlists) { + "passed3" +} else { + "failed" +} + +$s4 = if contains([42, 13, 0,], $intlists) { + "failed" +} else { + "passed4" +} + +test $s1 {} # passed +test $s2 {} # passed +test $s3 {} # passed +test $s4 {} # passed