From 6de7d8b254a1b422f7da27451b1efa07ff4bec4a Mon Sep 17 00:00:00 2001 From: James Shubin Date: Wed, 31 Jul 2024 16:01:27 -0400 Subject: [PATCH] lang: funcs: Catch non-specific type build error If you had ambiguous code, and specified an invalid type, this could sneak through and become a runtime error, instead of a compile-time error. We fix this and add a test. --- examples/lang/datetime2.mcl | 2 +- examples/lang/datetime3.mcl | 2 +- lang/funcs/struct_lookup_func.go | 8 ++++++++ .../TestAstFunc2/unify-template.txtar | 13 +++++++++++++ 4 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 lang/interpret_test/TestAstFunc2/unify-template.txtar diff --git a/examples/lang/datetime2.mcl b/examples/lang/datetime2.mcl index 97ba4859..bdcaa280 100644 --- a/examples/lang/datetime2.mcl +++ b/examples/lang/datetime2.mcl @@ -8,7 +8,7 @@ $ayear = 60 * 60 * 24 * 365 # is a year in seconds (31536000) $tmplvalues = struct{year => $secplusone, load => $theload,} -$theload = sys.load()->x1 +$theload float = sys.load()->x1 # ambiguous so we specify the type! if 5 > 3 { file "/tmp/mgmt/datetime" { diff --git a/examples/lang/datetime3.mcl b/examples/lang/datetime3.mcl index 53de40fa..ccfbbf1f 100644 --- a/examples/lang/datetime3.mcl +++ b/examples/lang/datetime3.mcl @@ -9,7 +9,7 @@ $ayear = 60 * 60 * 24 * 365 # is a year in seconds (31536000) $tmplvalues = struct{year => $secplusone, load => $theload, vumeter => $vumeter,} -$theload = sys.load()->x1 +$theload float = sys.load()->x1 # ambiguous so we specify the type! $vumeter = example.vumeter("====", 10, 0.9) diff --git a/lang/funcs/struct_lookup_func.go b/lang/funcs/struct_lookup_func.go index a9c8290e..d63bac75 100644 --- a/lang/funcs/struct_lookup_func.go +++ b/lang/funcs/struct_lookup_func.go @@ -193,6 +193,14 @@ func (obj *StructLookupFunc) Build(typ *types.Type) (*types.Type, error) { if ix == -1 { return nil, fmt.Errorf("field %s was not found in struct", obj.field) } + tF, exists := tStruct.Map[tStruct.Ord[ix]] + if !exists { + return nil, fmt.Errorf("field %s was not found in struct", obj.field) + } + // The return value must match the type of the field we're pulling out! + if err := typ.Out.Cmp(tF); err != nil { + return nil, fmt.Errorf("field %s type error: %+v", obj.field, err) + } obj.Type = tStruct // struct type obj.Out = typ.Out // type of return value diff --git a/lang/interpret_test/TestAstFunc2/unify-template.txtar b/lang/interpret_test/TestAstFunc2/unify-template.txtar new file mode 100644 index 00000000..83343b14 --- /dev/null +++ b/lang/interpret_test/TestAstFunc2/unify-template.txtar @@ -0,0 +1,13 @@ +-- main.mcl -- +import "sys" + +$tmplvalues = struct{num => 42, load => $theload,} + +$theload bool = sys.load()->x1 # wrong type, make sure the compiler catches it! + +file "/tmp/datetime" { + state => $const.res.file.state.exists, + content => template("num: {{ .num }} seconds\nload average: {{ .load }}\n", $tmplvalues), +} +-- OUTPUT -- +# err: errUnify: error setting type: func() { }, error: field x1 type error: base kind does not match (bool != float)