diff --git a/compiler/compiler.go b/compiler/compiler.go index f72913e3f5..2ec1c50051 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -493,6 +493,11 @@ func compile(n semantic.Node, subst map[uint64]semantic.MonoType, scope Scope) ( return &integerEvaluator{ i: n.Value, }, nil + case *semantic.PolyNumericLiteral: + return &integerEvaluator{ + i: n.Value, + }, nil + case *semantic.UnsignedIntegerLiteral: return &unsignedIntegerEvaluator{ i: n.Value, diff --git a/docs/SPEC.md b/docs/SPEC.md index da96990c10..18fbabbbb6 100644 --- a/docs/SPEC.md +++ b/docs/SPEC.md @@ -616,6 +616,11 @@ Int, Uint, and Float types are Divisible. Int, Uint, and Float types are Numeric. +##### Numeric Default Int Constraint + +Integer literals are polymorphic and can be interpreted as Int, Uint, or Float types. +This constraint is used to infer the type of integer literals based on the context of the program. + ##### Comparable Constraint Comparable types are those the binary comparison operators `<`, `<=`, `>`, or `>=` accept. diff --git a/internal/fbsemantic/semantic.fbs b/internal/fbsemantic/semantic.fbs index 66a40ba6c1..1c2a5775ed 100644 --- a/internal/fbsemantic/semantic.fbs +++ b/internal/fbsemantic/semantic.fbs @@ -94,6 +94,7 @@ enum Kind : ubyte { Record, Negatable, Timeable, + NumericDefaultInt, } table Constraint { @@ -133,6 +134,7 @@ union Expression { DateTimeLiteral, DurationLiteral, FloatLiteral, + PolyNumericLiteral, IntegerLiteral, StringLiteral, RegexpLiteral, @@ -397,6 +399,12 @@ table DurationLiteral { value:[Duration]; } +table PolyNumericLiteral { + loc:SourceLocation; + value:int64; + typ:MonoType; +} + table IntegerLiteral { loc:SourceLocation; value:int64; diff --git a/internal/fbsemantic/semantic_generated.go b/internal/fbsemantic/semantic_generated.go index 154ffbb471..3133a3f06b 100644 --- a/internal/fbsemantic/semantic_generated.go +++ b/internal/fbsemantic/semantic_generated.go @@ -55,29 +55,31 @@ var EnumNamesType = map[Type]string{ type Kind = byte const ( - KindAddable Kind = 0 - KindSubtractable Kind = 1 - KindDivisible Kind = 2 - KindNumeric Kind = 3 - KindComparable Kind = 4 - KindEquatable Kind = 5 - KindNullable Kind = 6 - KindRecord Kind = 7 - KindNegatable Kind = 8 - KindTimeable Kind = 9 + KindAddable Kind = 0 + KindSubtractable Kind = 1 + KindDivisible Kind = 2 + KindNumeric Kind = 3 + KindComparable Kind = 4 + KindEquatable Kind = 5 + KindNullable Kind = 6 + KindRecord Kind = 7 + KindNegatable Kind = 8 + KindTimeable Kind = 9 + KindNumericDefaultInt Kind = 10 ) var EnumNamesKind = map[Kind]string{ - KindAddable: "Addable", - KindSubtractable: "Subtractable", - KindDivisible: "Divisible", - KindNumeric: "Numeric", - KindComparable: "Comparable", - KindEquatable: "Equatable", - KindNullable: "Nullable", - KindRecord: "Record", - KindNegatable: "Negatable", - KindTimeable: "Timeable", + KindAddable: "Addable", + KindSubtractable: "Subtractable", + KindDivisible: "Divisible", + KindNumeric: "Numeric", + KindComparable: "Comparable", + KindEquatable: "Equatable", + KindNullable: "Nullable", + KindRecord: "Record", + KindNegatable: "Negatable", + KindTimeable: "Timeable", + KindNumericDefaultInt: "NumericDefaultInt", } type Statement = byte @@ -138,10 +140,11 @@ const ( ExpressionDateTimeLiteral Expression = 14 ExpressionDurationLiteral Expression = 15 ExpressionFloatLiteral Expression = 16 - ExpressionIntegerLiteral Expression = 17 - ExpressionStringLiteral Expression = 18 - ExpressionRegexpLiteral Expression = 19 - ExpressionUnsignedIntegerLiteral Expression = 20 + ExpressionPolyNumericLiteral Expression = 17 + ExpressionIntegerLiteral Expression = 18 + ExpressionStringLiteral Expression = 19 + ExpressionRegexpLiteral Expression = 20 + ExpressionUnsignedIntegerLiteral Expression = 21 ) var EnumNamesExpression = map[Expression]string{ @@ -162,6 +165,7 @@ var EnumNamesExpression = map[Expression]string{ ExpressionDateTimeLiteral: "DateTimeLiteral", ExpressionDurationLiteral: "DurationLiteral", ExpressionFloatLiteral: "FloatLiteral", + ExpressionPolyNumericLiteral: "PolyNumericLiteral", ExpressionIntegerLiteral: "IntegerLiteral", ExpressionStringLiteral: "StringLiteral", ExpressionRegexpLiteral: "RegexpLiteral", @@ -4168,6 +4172,91 @@ func DurationLiteralEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT { return builder.EndObject() } +type PolyNumericLiteral struct { + _tab flatbuffers.Table +} + +func GetRootAsPolyNumericLiteral(buf []byte, offset flatbuffers.UOffsetT) *PolyNumericLiteral { + n := flatbuffers.GetUOffsetT(buf[offset:]) + x := &PolyNumericLiteral{} + x.Init(buf, n+offset) + return x +} + +func (rcv *PolyNumericLiteral) Init(buf []byte, i flatbuffers.UOffsetT) { + rcv._tab.Bytes = buf + rcv._tab.Pos = i +} + +func (rcv *PolyNumericLiteral) Table() flatbuffers.Table { + return rcv._tab +} + +func (rcv *PolyNumericLiteral) Loc(obj *SourceLocation) *SourceLocation { + o := flatbuffers.UOffsetT(rcv._tab.Offset(4)) + if o != 0 { + x := rcv._tab.Indirect(o + rcv._tab.Pos) + if obj == nil { + obj = new(SourceLocation) + } + obj.Init(rcv._tab.Bytes, x) + return obj + } + return nil +} + +func (rcv *PolyNumericLiteral) Value() int64 { + o := flatbuffers.UOffsetT(rcv._tab.Offset(6)) + if o != 0 { + return rcv._tab.GetInt64(o + rcv._tab.Pos) + } + return 0 +} + +func (rcv *PolyNumericLiteral) MutateValue(n int64) bool { + return rcv._tab.MutateInt64Slot(6, n) +} + +func (rcv *PolyNumericLiteral) TypType() byte { + o := flatbuffers.UOffsetT(rcv._tab.Offset(8)) + if o != 0 { + return rcv._tab.GetByte(o + rcv._tab.Pos) + } + return 0 +} + +func (rcv *PolyNumericLiteral) MutateTypType(n byte) bool { + return rcv._tab.MutateByteSlot(8, n) +} + +func (rcv *PolyNumericLiteral) Typ(obj *flatbuffers.Table) bool { + o := flatbuffers.UOffsetT(rcv._tab.Offset(10)) + if o != 0 { + rcv._tab.Union(obj, o) + return true + } + return false +} + +func PolyNumericLiteralStart(builder *flatbuffers.Builder) { + builder.StartObject(4) +} +func PolyNumericLiteralAddLoc(builder *flatbuffers.Builder, loc flatbuffers.UOffsetT) { + builder.PrependUOffsetTSlot(0, flatbuffers.UOffsetT(loc), 0) +} +func PolyNumericLiteralAddValue(builder *flatbuffers.Builder, value int64) { + builder.PrependInt64Slot(1, value, 0) +} +func PolyNumericLiteralAddTypType(builder *flatbuffers.Builder, typType byte) { + builder.PrependByteSlot(2, typType, 0) +} +func PolyNumericLiteralAddTyp(builder *flatbuffers.Builder, typ flatbuffers.UOffsetT) { + builder.PrependUOffsetTSlot(3, flatbuffers.UOffsetT(typ), 0) +} +func PolyNumericLiteralEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT { + return builder.EndObject() +} + type IntegerLiteral struct { _tab flatbuffers.Table } diff --git a/interpreter/interpreter.go b/interpreter/interpreter.go index dd6836c4f0..9a14cfe98b 100644 --- a/interpreter/interpreter.go +++ b/interpreter/interpreter.go @@ -575,7 +575,7 @@ func (itrp *Interpreter) doLiteral(lit semantic.Literal) (values.Value, error) { return values.NewDuration(dur), nil case *semantic.FloatLiteral: return values.NewFloat(l.Value), nil - case *semantic.IntegerLiteral: + case *semantic.PolyNumericLiteral: return values.NewInt(l.Value), nil case *semantic.UnsignedIntegerLiteral: return values.NewUInt(l.Value), nil @@ -1140,7 +1140,7 @@ func resolveValue(v values.Value) (semantic.Node, bool, error) { Value: v.Str(), }, true, nil case semantic.Int: - return &semantic.IntegerLiteral{ + return &semantic.PolyNumericLiteral{ Value: v.Int(), }, true, nil case semantic.UInt: diff --git a/interpreter/interpreter_test.go b/interpreter/interpreter_test.go index 99f1d21592..e598456719 100644 --- a/interpreter/interpreter_test.go +++ b/interpreter/interpreter_test.go @@ -53,14 +53,14 @@ func TestEval(t *testing.T) { { name: "string interpolation missing field", query: ` - r = makeRecord(o: {a: "foo", b: 42}) + r = o: {a: "foo", b: 42} "r._value = ${r._value}"`, wantErr: any, }, { name: "string interpolation field has wrong type", query: ` - r = makeRecord(o: {a: "foo", b: 42}) + r = o: {a: "foo", b: 42} "r._value = ${r.b}"`, wantErr: any, }, @@ -287,7 +287,7 @@ func TestEval(t *testing.T) { name: "array index expression out of bounds high", query: ` a = [1, 2, 3] - i = 3 + i = 3 x = a[i] `, wantErr: any, @@ -439,31 +439,31 @@ func TestEval_Operator_Precedence(t *testing.T) { want values.Value }{ { - src: "2.0 * 3.0 ^ 2.0", + src: "2 * 3 ^ 2", want: values.NewFloat(18.0), }, { - src: "(2.0 * 3.0) ^ 2.0", + src: "(2 * 3) ^ 2", want: values.NewFloat(36.0), }, { - src: "4.0 / 2.0 ^ 2.0", + src: "4 / 2 ^ 2", want: values.NewFloat(1.0), }, { - src: "(4.0 / 2.0) ^ 2.0", + src: "(4 / 2) ^ 2", want: values.NewFloat(4.0), }, { - src: "2.0 % 4.0 ^ 2.0", + src: "2 % 4 ^ 2", want: values.NewFloat(2.0), }, { - src: "(2.0 % 4.0) ^ 2.0", + src: "(2 % 4) ^ 2", want: values.NewFloat(4.0), }, { - src: "1.0 + 2.0 * 3.0", + src: "1 + 2 * 3.0", want: values.NewFloat(7.0), }, { @@ -503,35 +503,35 @@ func TestEval_Operator_Precedence(t *testing.T) { want: values.NewFloat(6.0), }, { - src: "1.0 + 2.0 < 4.0", + src: "1 + 2 < 4", want: values.NewBool(true), }, { - src: "(1.0 + 2.0) < 4.0", + src: "(1 + 2) < 4", want: values.NewBool(true), }, { - src: "1.0 + 2.0 <= 4.0", + src: "1 + 2 <= 4", want: values.NewBool(true), }, { - src: "(1.0 + 2.0) <= 4.0", + src: "(1 + 2) <= 4", want: values.NewBool(true), }, { - src: "1.0 + 2.0 > 4.0", + src: "1 + 2 > 4", want: values.NewBool(false), }, { - src: "(1.0 + 2.0) > 4.0", + src: "(1 + 2) > 4", want: values.NewBool(false), }, { - src: "1.0 + 2.0 >= 4.0", + src: "1 + 2 >= 4", want: values.NewBool(false), }, { - src: "(1.0 + 2.0) >= 4.0", + src: "(1 + 2) >= 4", want: values.NewBool(false), }, { @@ -666,9 +666,9 @@ func TestInterpreter_MultipleEval(t *testing.T) { Value: values.NewInt(2), Node: &semantic.ExpressionStatement{ Expression: &semantic.BinaryExpression{ - Left: &semantic.IntegerLiteral{Value: 1}, + Left: &semantic.PolyNumericLiteral{Value: 1}, Operator: ast.AdditionOperator, - Right: &semantic.IntegerLiteral{Value: 1}, + Right: &semantic.PolyNumericLiteral{Value: 1}, }, }, }, @@ -686,9 +686,9 @@ func TestInterpreter_MultipleEval(t *testing.T) { Value: values.NewInt(2), Node: &semantic.ExpressionStatement{ Expression: &semantic.BinaryExpression{ - Left: &semantic.IntegerLiteral{Value: 1}, + Left: &semantic.PolyNumericLiteral{Value: 1}, Operator: ast.AdditionOperator, - Right: &semantic.IntegerLiteral{Value: 1}, + Right: &semantic.PolyNumericLiteral{Value: 1}, }, }, }, @@ -709,7 +709,7 @@ func TestInterpreter_MultipleEval(t *testing.T) { Property: "yield", }, Arguments: &semantic.ObjectExpression{Properties: []*semantic.Property{}}, - Pipe: &semantic.IntegerLiteral{Value: 0}, + Pipe: &semantic.PolyNumericLiteral{Value: 0}, }, }, { diff --git a/libflux/c/main.c b/libflux/c/main.c index e9c8a158a5..b00618873b 100644 --- a/libflux/c/main.c +++ b/libflux/c/main.c @@ -85,7 +85,7 @@ void test_semantic() { { printf("Parsing to AST\n"); - struct flux_ast_pkg_t *ast_pkg_foo = flux_parse("test", "package foo\nx = 1 + 1.0"); + struct flux_ast_pkg_t *ast_pkg_foo = flux_parse("test", "package foo\nx = 1 + \"1.0\""); assert(ast_pkg_foo != NULL); printf("Analyzing (expect failure)\n"); @@ -115,7 +115,7 @@ void test_semantic() { { printf("Parsing to AST\n"); - struct flux_ast_pkg_t *ast_pkg_foo = flux_parse("test", "package foo\nx = 1 + 1.0"); + struct flux_ast_pkg_t *ast_pkg_foo = flux_parse("test", "package foo\nx = 1 + \"1.0\""); assert(ast_pkg_foo != NULL); printf("Find variable type v (expect failure)\n"); struct flux_buffer_t buf; diff --git a/libflux/go/libflux/analyze_test.go b/libflux/go/libflux/analyze_test.go index 7a12bec52c..b2dfc280bc 100644 --- a/libflux/go/libflux/analyze_test.go +++ b/libflux/go/libflux/analyze_test.go @@ -26,7 +26,7 @@ func TestAnalyze(t *testing.T) { { name: "failure", flx: `x = "foo" + 10`, - err: errors.New("type error @1:13-1:15: expected string but found int"), + err: errors.New("type error @1:13-1:15: string is not NumericDefaultInt"), }, } for _, tc := range tcs { @@ -86,12 +86,12 @@ s = {v with tmp: 1} // construct a new struct with "object with" m = s.tmp // not belong to "v" p = s.timeRangeStop // transitive reference `, - ty: "{int: int | num: t0 | self: t1 | str: string | sweet: t2 | timeRangeStop: t3 | t4}", + ty: "{int: t0 | num: t1 | self: t2 | str: string | sweet: t3 | timeRangeStop: t4 | t5}", }, { name: "failure", flx: `x = "foo" + 10`, - err: errors.New("type error @1:13-1:15: expected string but found int"), + err: errors.New("type error @1:13-1:15: string is not NumericDefaultInt"), }, } for _, tc := range tcs { diff --git a/libflux/go/libflux/buildinfo.gen.go b/libflux/go/libflux/buildinfo.gen.go index 30ce1a5755..9579fe4f30 100644 --- a/libflux/go/libflux/buildinfo.gen.go +++ b/libflux/go/libflux/buildinfo.gen.go @@ -42,25 +42,25 @@ var sourceHashes = map[string]string{ "libflux/src/core/scanner/unicode.rl.COPYING": "6cf2d5d26d52772ded8a5f0813f49f83dfa76006c5f398713be3854fe7bc4c7e", "libflux/src/core/semantic/bootstrap.rs": "e6935092bc7cae45caab02dc9dd4725b4791fb5e2cea01555933cfa14edfc0e8", "libflux/src/core/semantic/check.rs": "acb29602ee01f636818ba3522b3f110018abca3e7b4a6b75c29eec97856a324e", - "libflux/src/core/semantic/convert.rs": "f0821be66ba8fd1e1a177bf63ad125778dbb5c7a78091a7c52452ab159aecbe4", + "libflux/src/core/semantic/convert.rs": "81faa6a2ebb3a624350f82fe02c2cd97eddd89edfbd2d11900e682f27f0f0fd8", "libflux/src/core/semantic/env.rs": "e031d5b752d207a8f93bacd8515639e832735d5a85e90db76690aaeee8168127", - "libflux/src/core/semantic/flatbuffers/mod.rs": "244afed2e7cee6dc5a80a03a1c91d38ad1137bc2163640b59f464f383d7a01d5", - "libflux/src/core/semantic/flatbuffers/semantic_generated.rs": "ebd91d5eaa43f2604724661a0ccbd1645dde70cfb9834e0e0889badb2b17317b", - "libflux/src/core/semantic/flatbuffers/tests.rs": "563d1c78bfc70473048611d1c4153951122aca030b775663f627f3754c806634", - "libflux/src/core/semantic/flatbuffers/types.rs": "cea742d4473952fb5767a7c2e746e6968b684b3fae6244440ca5a8b6a389435a", + "libflux/src/core/semantic/flatbuffers/mod.rs": "c16a128150b1be38b9a2e7c863d85800c576dfee89b06efae869a2ff9c54ae46", + "libflux/src/core/semantic/flatbuffers/semantic_generated.rs": "a7b809c7ebb63d5b5a0af7f931b69ecc0d3e629c0098d9230f936ab28df6053f", + "libflux/src/core/semantic/flatbuffers/tests.rs": "7f0293774f2be1eb865234202a46190199e5295ebb91d6a5746cb0950c434a34", + "libflux/src/core/semantic/flatbuffers/types.rs": "92c979adebf156cd31a21b9a41417aa3b948a5201b97a42f02f1fe173304ca38", "libflux/src/core/semantic/fresh.rs": "896e8132cdb731dbc70cdbc465c5020099af30dbe954be715f78a2b4cd8b738b", "libflux/src/core/semantic/import.rs": "7b0a9259bb86fc887707ac442a71ca63ea1ef61b150a376d973d5dd3172e6ec6", "libflux/src/core/semantic/infer.rs": "23fd3349fb08e950d521061c4bd7a10673816707bd7f26351aa959572dc0a408", - "libflux/src/core/semantic/mod.rs": "4d63d0471461c0f1baece0d2828ae237312d23707cdb17c59565704ca93195d2", - "libflux/src/core/semantic/nodes.rs": "bdf1a719fe6bec49fb5578c46c9ca938ac351590e0099bb39b5ed3e65d00eaf8", - "libflux/src/core/semantic/sub.rs": "9fd72f30c4ea5fde11c636160dc88271d34d6f49e2c5f95d32d22a500ef109b9", - "libflux/src/core/semantic/tests.rs": "bcb6c4031334f127235fb7baef10b47239ab617a3a3fc9687f0c14199612dfc7", - "libflux/src/core/semantic/types.rs": "aa5f8dbc4267badfda38461aa62ef27e3dbb7e1b7d3def695c71df2a499c0c45", - "libflux/src/core/semantic/walk/_walk.rs": "f13a98ba71c05b960bae2db66d87f30368bd1deea3b1d0013604afb7c7bba8b8", + "libflux/src/core/semantic/mod.rs": "1c352965c47f356abe04c6104fce67f42cdc96da2a9fe6b2b93e18117e642f78", + "libflux/src/core/semantic/nodes.rs": "b9563ffdb0c17dc688b17725ef74e14433b93aefa20ae570e17124d3040fd033", + "libflux/src/core/semantic/sub.rs": "94190a8bf4f102e07cf7f1dc1dfc6e65d077eaf26bd0a89468aac0e4870c58b5", + "libflux/src/core/semantic/tests.rs": "2a8bdb957d6407c922a1bb3ed8a734ed927ec1e37fdbbf914d29ca1dc4c49acd", + "libflux/src/core/semantic/types.rs": "18c5ce8131322ef889a2b3ec5743edce7bfbda8f01e702d18ad5fcd467a6dcd7", + "libflux/src/core/semantic/walk/_walk.rs": "c0379f93dd9aa250e84e160fd3841be8dfd67a650eedfd456a6803c2a34689f8", "libflux/src/core/semantic/walk/mod.rs": "f0167952d639b075c45212d895aee87997e477a76446912a29ab6ed6a9c21325", "libflux/src/core/semantic/walk/test_utils.rs": "317606227b28f607273e4ff6ac431e148f753cc20ea1812fa7b97ecb262b3f90", - "libflux/src/core/semantic/walk/walk_mut.rs": "4a4614a0459d07dbd4f0579028acf3897b82a9d3fa6ed59511c4512d786e1de6", - "libflux/src/core/tests/analyze_test.rs": "c49d81bdaa2275681990cdf8e18eb13adcbb54895ba0c2c49ab1a727d529a820", + "libflux/src/core/semantic/walk/walk_mut.rs": "bfe2bd402875c9c30c42a24c209ce79973dc402718f895e8f59e02b6f99c7220", + "libflux/src/core/tests/analyze_test.rs": "7fec4c1fe8dd12c029a7b1383d3c107dee990bd53c145aa47432af54b21db978", "libflux/src/flux/Cargo.toml": "5b39be929c9bdfceee73fe7d141b420139170d520e43b9013e6dd8f27916ab0f", "libflux/src/flux/benches/.gitattributes": "d6163f7f0c0983b4ef86846d9e4daa63ad184f189fce572368bbd17d7a7360a9", "libflux/src/flux/benches/basic.rs": "f6b1d133a4edaa38b0ab6e2b63de1bbd62a489e39b77c62d0df6fa8e5462da0e", @@ -68,7 +68,7 @@ var sourceHashes = map[string]string{ "libflux/src/flux/benches/everything.flux": "8fca377299a33e610fc435098c770ea0974b61150d71b9c5f206ce87c0a57656", "libflux/src/flux/benches/formatter.rs": "e4838c1428d45205292a9dda1759f804779a4e142db4360ec2a6be1c9ca741b8", "libflux/src/flux/build.rs": "31a4f825297f9b79d1c8692a5fa3ff9211cb87d01650d147128d061588f75abd", - "libflux/src/flux/lib.rs": "2f94a3dfffa54d355811e8709451150a4d61d00dd2b0dec4ce44580a43d6eb78", + "libflux/src/flux/lib.rs": "f4d48033d38b084012fd0d3cf562c7e1041deb5514de78eb7a4a3e2b46bc8a19", "stdlib/contrib/RohanSreerama5/naiveBayesClassifier/bayes_test.flux": "d22b87db3818b7c628e0a88eaabbbd882be002a6260f8c9e77a842a2d3f0c120", "stdlib/contrib/RohanSreerama5/naiveBayesClassifier/naiveBayesClassifier.flux": "453386ba1a814fe9e89cb97e66f1148954517097dc326f0a6990d1847132cc49", "stdlib/contrib/anaisdg/anomalydetection/mad.flux": "5171847f73987c2d6457b1b8f129591f2dd6c1c95774a5244ccffeb9ad493cd4", diff --git a/libflux/src/core/semantic/convert.rs b/libflux/src/core/semantic/convert.rs index 643cf4ab43..e1e9519850 100644 --- a/libflux/src/core/semantic/convert.rs +++ b/libflux/src/core/semantic/convert.rs @@ -260,6 +260,7 @@ pub fn convert_polytype( "Negatable" => kinds.push(types::Kind::Negatable), "Timeable" => kinds.push(types::Kind::Timeable), "Record" => kinds.push(types::Kind::Record), + "NumericDefaultInt" => kinds.push(types::Kind::NumericDefaultInt), _ => { return Err(format!("Constraint not found {} ", &k.name.as_str())); } @@ -331,7 +332,7 @@ fn convert_expression(expr: ast::Expression, fresher: &mut Fresher) -> Result Ok(Expression::StringLit(convert_string_literal(lit, fresher)?)), ast::Expression::Boolean(lit) => Ok(Expression::Boolean(convert_boolean_literal(lit, fresher)?)), ast::Expression::Float(lit) => Ok(Expression::Float(convert_float_literal(lit, fresher)?)), - ast::Expression::Integer(lit) => Ok(Expression::Integer(convert_integer_literal(lit, fresher)?)), + ast::Expression::Integer(lit) => Ok(Expression::PolyNumeric(convert_integer_literal(lit, fresher)?)), ast::Expression::Uint(lit) => Ok(Expression::Uint(convert_unsigned_integer_literal(lit, fresher)?)), ast::Expression::Regexp(lit) => Ok(Expression::Regexp(convert_regexp_literal(lit, fresher)?)), ast::Expression::Duration(lit) => Ok(Expression::Duration(convert_duration_literal(lit, fresher)?)), @@ -666,10 +667,11 @@ fn convert_float_literal(lit: ast::FloatLit, _: &mut Fresher) -> Result Result { - Ok(IntegerLit { +fn convert_integer_literal(lit: ast::IntegerLit, fresher: &mut Fresher) -> Result { + Ok(PolyNumericLit { loc: lit.base.location, value: lit.value, + typ: MonoType::Var(fresher.fresh()), }) } @@ -992,9 +994,10 @@ mod tests { loc: b.location.clone(), name: "a".to_string(), }, - value: Expression::Integer(IntegerLit { + value: Expression::PolyNumeric(PolyNumericLit { loc: b.location.clone(), value: 10, + typ: MonoType::Var(Tvar(0)), }), }], })), @@ -1062,9 +1065,10 @@ mod tests { loc: b.location.clone(), name: "a".to_string(), }, - value: Expression::Integer(IntegerLit { + value: Expression::PolyNumeric(PolyNumericLit { loc: b.location.clone(), value: 10, + typ: MonoType::Var(Tvar(0)), }), }], })), @@ -1148,9 +1152,10 @@ mod tests { loc: b.location.clone(), name: "a".to_string(), }, - value: Expression::Integer(IntegerLit { + value: Expression::PolyNumeric(PolyNumericLit { loc: b.location.clone(), value: 10, + typ: MonoType::Var(Tvar(0)), }), }, Property { @@ -1159,9 +1164,10 @@ mod tests { loc: b.location.clone(), name: "b".to_string(), }, - value: Expression::Integer(IntegerLit { + value: Expression::PolyNumeric(PolyNumericLit { loc: b.location.clone(), value: 11, + typ: MonoType::Var(Tvar(0)), }), }, ], @@ -1449,9 +1455,10 @@ mod tests { loc: b.location.clone(), name: "retry".to_string(), }, - value: Expression::Integer(IntegerLit { + value: Expression::PolyNumeric(PolyNumericLit { loc: b.location.clone(), value: 5, + typ: MonoType::Var(Tvar(0)), }), }, ], @@ -1725,9 +1732,10 @@ mod tests { loc: b.location.clone(), name: "a".to_string(), }, - value: Expression::Integer(IntegerLit { + value: Expression::PolyNumeric(PolyNumericLit { loc: b.location.clone(), value: 2, + typ: MonoType::Var(Tvar(0)), }), }, Property { @@ -1736,9 +1744,10 @@ mod tests { loc: b.location.clone(), name: "b".to_string(), }, - value: Expression::Integer(IntegerLit { + value: Expression::PolyNumeric(PolyNumericLit { loc: b.location.clone(), value: 3, + typ: MonoType::Var(Tvar(0)), }), }, ], @@ -1897,9 +1906,10 @@ mod tests { loc: b.location.clone(), name: "a".to_string(), }, - default: Some(Expression::Integer(IntegerLit { + default: Some(Expression::PolyNumeric(PolyNumericLit { loc: b.location.clone(), value: 0, + typ: MonoType::Var(Tvar(0)), })), }, FunctionParameter { @@ -1909,9 +1919,10 @@ mod tests { loc: b.location.clone(), name: "b".to_string(), }, - default: Some(Expression::Integer(IntegerLit { + default: Some(Expression::PolyNumeric(PolyNumericLit { loc: b.location.clone(), value: 0, + typ: MonoType::Var(Tvar(0)), })), }, FunctionParameter { @@ -1972,9 +1983,10 @@ mod tests { loc: b.location.clone(), name: "c".to_string(), }, - value: Expression::Integer(IntegerLit { + value: Expression::PolyNumeric(PolyNumericLit { loc: b.location.clone(), value: 42, + typ: MonoType::Var(Tvar(0)), }), }], })), @@ -2309,9 +2321,10 @@ mod tests { expression: Expression::Call(Box::new(CallExpr { loc: b.location.clone(), typ: type_info(), - pipe: Some(Expression::Integer(IntegerLit { + pipe: Some(Expression::PolyNumeric(PolyNumericLit { loc: b.location.clone(), value: 3, + typ: MonoType::Var(Tvar(0)), })), callee: Expression::Identifier(IdentifierExpr { loc: b.location.clone(), @@ -2324,9 +2337,10 @@ mod tests { loc: b.location.clone(), name: "a".to_string(), }, - value: Expression::Integer(IntegerLit { + value: Expression::PolyNumeric(PolyNumericLit { loc: b.location.clone(), value: 2, + typ: MonoType::Var(Tvar(0)), }), }], })), @@ -2397,9 +2411,10 @@ mod tests { loc: b.location.clone(), name: "a".to_string(), }, - default: Some(Expression::Integer(IntegerLit { + default: Some(Expression::PolyNumeric(PolyNumericLit { loc: b.location.clone(), value: 0, + typ: MonoType::Var(Tvar(0)), })), }; let default1 = FunctionParameter { @@ -2409,9 +2424,10 @@ mod tests { loc: b.location.clone(), name: "b".to_string(), }, - default: Some(Expression::Integer(IntegerLit { + default: Some(Expression::PolyNumeric(PolyNumericLit { loc: b.location.clone(), value: 1, + typ: MonoType::Var(Tvar(0)), })), }; let default2 = FunctionParameter { @@ -2421,9 +2437,10 @@ mod tests { loc: b.location.clone(), name: "c".to_string(), }, - default: Some(Expression::Integer(IntegerLit { + default: Some(Expression::PolyNumeric(PolyNumericLit { loc: b.location.clone(), value: 2, + typ: MonoType::Var(Tvar(0)), })), }; let no_default = FunctionParameter { @@ -2517,9 +2534,10 @@ mod tests { typ: type_info(), name: "a".to_string(), }), - index: Expression::Integer(IntegerLit { + index: Expression::PolyNumeric(PolyNumericLit { loc: b.location.clone(), value: 3, + typ: MonoType::Var(Tvar(0)), }), })), })], @@ -2590,14 +2608,16 @@ mod tests { typ: type_info(), name: "a".to_string(), }), - index: Expression::Integer(IntegerLit { + index: Expression::PolyNumeric(PolyNumericLit { loc: b.location.clone(), value: 3, + typ: MonoType::Var(Tvar(0)), }), })), - index: Expression::Integer(IntegerLit { + index: Expression::PolyNumeric(PolyNumericLit { loc: b.location.clone(), value: 5, + typ: MonoType::Var(Tvar(0)), }), })), })], @@ -2668,9 +2688,10 @@ mod tests { }), arguments: Vec::new(), })), - index: Expression::Integer(IntegerLit { + index: Expression::PolyNumeric(PolyNumericLit { loc: b.location.clone(), value: 3, + typ: MonoType::Var(Tvar(0)), }), })), })], diff --git a/libflux/src/core/semantic/flatbuffers/mod.rs b/libflux/src/core/semantic/flatbuffers/mod.rs index 6b80b9baf4..270379892b 100644 --- a/libflux/src/core/semantic/flatbuffers/mod.rs +++ b/libflux/src/core/semantic/flatbuffers/mod.rs @@ -50,6 +50,24 @@ impl<'a> semantic::walk::Visitor<'_> for SerializingVisitor<'a> { let node = &*node; let loc = v.create_loc(node.loc()); match node { + walk::Node::PolyNumericLit(int) => { + let int_typ = int.typ.clone(); + let (typ, typ_type) = types::build_type(&mut v.builder, int_typ); + let int = fbsemantic::PolyNumericLiteral::create( + &mut v.builder, + &fbsemantic::PolyNumericLiteralArgs { + loc, + value: int.value, + typ: Some(typ), + typ_type, + }, + ); + v.expr_stack.push(( + int.as_union_value(), + fbsemantic::Expression::PolyNumericLiteral, + )) + } + walk::Node::IntegerLit(int) => { let int = fbsemantic::IntegerLiteral::create( &mut v.builder, diff --git a/libflux/src/core/semantic/flatbuffers/semantic_generated.rs b/libflux/src/core/semantic/flatbuffers/semantic_generated.rs index 7ee34b4995..817b9d001c 100644 --- a/libflux/src/core/semantic/flatbuffers/semantic_generated.rs +++ b/libflux/src/core/semantic/flatbuffers/semantic_generated.rs @@ -167,10 +167,11 @@ pub mod fbsemantic { Record = 7, Negatable = 8, Timeable = 9, + NumericDefaultInt = 10, } const ENUM_MIN_KIND: u8 = 0; - const ENUM_MAX_KIND: u8 = 9; + const ENUM_MAX_KIND: u8 = 10; impl<'a> flatbuffers::Follow<'a> for Kind { type Inner = Self; @@ -204,7 +205,7 @@ pub mod fbsemantic { } #[allow(non_camel_case_types)] - const ENUM_VALUES_KIND: [Kind; 10] = [ + const ENUM_VALUES_KIND: [Kind; 11] = [ Kind::Addable, Kind::Subtractable, Kind::Divisible, @@ -215,10 +216,11 @@ pub mod fbsemantic { Kind::Record, Kind::Negatable, Kind::Timeable, + Kind::NumericDefaultInt, ]; #[allow(non_camel_case_types)] - const ENUM_NAMES_KIND: [&'static str; 10] = [ + const ENUM_NAMES_KIND: [&'static str; 11] = [ "Addable", "Subtractable", "Divisible", @@ -229,6 +231,7 @@ pub mod fbsemantic { "Record", "Negatable", "Timeable", + "NumericDefaultInt", ]; pub fn enum_name_kind(e: Kind) -> &'static str { @@ -395,14 +398,15 @@ pub mod fbsemantic { DateTimeLiteral = 14, DurationLiteral = 15, FloatLiteral = 16, - IntegerLiteral = 17, - StringLiteral = 18, - RegexpLiteral = 19, - UnsignedIntegerLiteral = 20, + PolyNumericLiteral = 17, + IntegerLiteral = 18, + StringLiteral = 19, + RegexpLiteral = 20, + UnsignedIntegerLiteral = 21, } const ENUM_MIN_EXPRESSION: u8 = 0; - const ENUM_MAX_EXPRESSION: u8 = 20; + const ENUM_MAX_EXPRESSION: u8 = 21; impl<'a> flatbuffers::Follow<'a> for Expression { type Inner = Self; @@ -436,7 +440,7 @@ pub mod fbsemantic { } #[allow(non_camel_case_types)] - const ENUM_VALUES_EXPRESSION: [Expression; 21] = [ + const ENUM_VALUES_EXPRESSION: [Expression; 22] = [ Expression::NONE, Expression::StringExpression, Expression::ArrayExpression, @@ -454,6 +458,7 @@ pub mod fbsemantic { Expression::DateTimeLiteral, Expression::DurationLiteral, Expression::FloatLiteral, + Expression::PolyNumericLiteral, Expression::IntegerLiteral, Expression::StringLiteral, Expression::RegexpLiteral, @@ -461,7 +466,7 @@ pub mod fbsemantic { ]; #[allow(non_camel_case_types)] - const ENUM_NAMES_EXPRESSION: [&'static str; 21] = [ + const ENUM_NAMES_EXPRESSION: [&'static str; 22] = [ "NONE", "StringExpression", "ArrayExpression", @@ -479,6 +484,7 @@ pub mod fbsemantic { "DateTimeLiteral", "DurationLiteral", "FloatLiteral", + "PolyNumericLiteral", "IntegerLiteral", "StringLiteral", "RegexpLiteral", @@ -3774,6 +3780,17 @@ pub mod fbsemantic { } } + #[inline] + #[allow(non_snake_case)] + pub fn expression_as_poly_numeric_literal(&self) -> Option> { + if self.expression_type() == Expression::PolyNumericLiteral { + self.expression() + .map(|u| PolyNumericLiteral::init_from_table(u)) + } else { + None + } + } + #[inline] #[allow(non_snake_case)] pub fn expression_as_integer_literal(&self) -> Option> { @@ -4112,6 +4129,17 @@ pub mod fbsemantic { } } + #[inline] + #[allow(non_snake_case)] + pub fn argument_as_poly_numeric_literal(&self) -> Option> { + if self.argument_type() == Expression::PolyNumericLiteral { + self.argument() + .map(|u| PolyNumericLiteral::init_from_table(u)) + } else { + None + } + } + #[inline] #[allow(non_snake_case)] pub fn argument_as_integer_literal(&self) -> Option> { @@ -4469,6 +4497,16 @@ pub mod fbsemantic { } } + #[inline] + #[allow(non_snake_case)] + pub fn init__as_poly_numeric_literal(&self) -> Option> { + if self.init__type() == Expression::PolyNumericLiteral { + self.init_().map(|u| PolyNumericLiteral::init_from_table(u)) + } else { + None + } + } + #[inline] #[allow(non_snake_case)] pub fn init__as_integer_literal(&self) -> Option> { @@ -4829,6 +4867,16 @@ pub mod fbsemantic { } } + #[inline] + #[allow(non_snake_case)] + pub fn init__as_poly_numeric_literal(&self) -> Option> { + if self.init__type() == Expression::PolyNumericLiteral { + self.init_().map(|u| PolyNumericLiteral::init_from_table(u)) + } else { + None + } + } + #[inline] #[allow(non_snake_case)] pub fn init__as_integer_literal(&self) -> Option> { @@ -5169,6 +5217,17 @@ pub mod fbsemantic { } } + #[inline] + #[allow(non_snake_case)] + pub fn expression_as_poly_numeric_literal(&self) -> Option> { + if self.expression_type() == Expression::PolyNumericLiteral { + self.expression() + .map(|u| PolyNumericLiteral::init_from_table(u)) + } else { + None + } + } + #[inline] #[allow(non_snake_case)] pub fn expression_as_integer_literal(&self) -> Option> { @@ -5643,6 +5702,19 @@ pub mod fbsemantic { } } + #[inline] + #[allow(non_snake_case)] + pub fn interpolated_expression_as_poly_numeric_literal( + &self, + ) -> Option> { + if self.interpolated_expression_type() == Expression::PolyNumericLiteral { + self.interpolated_expression() + .map(|u| PolyNumericLiteral::init_from_table(u)) + } else { + None + } + } + #[inline] #[allow(non_snake_case)] pub fn interpolated_expression_as_integer_literal(&self) -> Option> { @@ -6436,6 +6508,17 @@ pub mod fbsemantic { } } + #[inline] + #[allow(non_snake_case)] + pub fn default_as_poly_numeric_literal(&self) -> Option> { + if self.default_type() == Expression::PolyNumericLiteral { + self.default() + .map(|u| PolyNumericLiteral::init_from_table(u)) + } else { + None + } + } + #[inline] #[allow(non_snake_case)] pub fn default_as_integer_literal(&self) -> Option> { @@ -6943,6 +7026,16 @@ pub mod fbsemantic { } } + #[inline] + #[allow(non_snake_case)] + pub fn left_as_poly_numeric_literal(&self) -> Option> { + if self.left_type() == Expression::PolyNumericLiteral { + self.left().map(|u| PolyNumericLiteral::init_from_table(u)) + } else { + None + } + } + #[inline] #[allow(non_snake_case)] pub fn left_as_integer_literal(&self) -> Option> { @@ -7146,6 +7239,16 @@ pub mod fbsemantic { } } + #[inline] + #[allow(non_snake_case)] + pub fn right_as_poly_numeric_literal(&self) -> Option> { + if self.right_type() == Expression::PolyNumericLiteral { + self.right().map(|u| PolyNumericLiteral::init_from_table(u)) + } else { + None + } + } + #[inline] #[allow(non_snake_case)] pub fn right_as_integer_literal(&self) -> Option> { @@ -7619,6 +7722,17 @@ pub mod fbsemantic { } } + #[inline] + #[allow(non_snake_case)] + pub fn callee_as_poly_numeric_literal(&self) -> Option> { + if self.callee_type() == Expression::PolyNumericLiteral { + self.callee() + .map(|u| PolyNumericLiteral::init_from_table(u)) + } else { + None + } + } + #[inline] #[allow(non_snake_case)] pub fn callee_as_integer_literal(&self) -> Option> { @@ -7822,6 +7936,16 @@ pub mod fbsemantic { } } + #[inline] + #[allow(non_snake_case)] + pub fn pipe_as_poly_numeric_literal(&self) -> Option> { + if self.pipe_type() == Expression::PolyNumericLiteral { + self.pipe().map(|u| PolyNumericLiteral::init_from_table(u)) + } else { + None + } + } + #[inline] #[allow(non_snake_case)] pub fn pipe_as_integer_literal(&self) -> Option> { @@ -8293,6 +8417,16 @@ pub mod fbsemantic { } } + #[inline] + #[allow(non_snake_case)] + pub fn test_as_poly_numeric_literal(&self) -> Option> { + if self.test_type() == Expression::PolyNumericLiteral { + self.test().map(|u| PolyNumericLiteral::init_from_table(u)) + } else { + None + } + } + #[inline] #[allow(non_snake_case)] pub fn test_as_integer_literal(&self) -> Option> { @@ -8507,6 +8641,17 @@ pub mod fbsemantic { } } + #[inline] + #[allow(non_snake_case)] + pub fn alternate_as_poly_numeric_literal(&self) -> Option> { + if self.alternate_type() == Expression::PolyNumericLiteral { + self.alternate() + .map(|u| PolyNumericLiteral::init_from_table(u)) + } else { + None + } + } + #[inline] #[allow(non_snake_case)] pub fn alternate_as_integer_literal(&self) -> Option> { @@ -8723,6 +8868,17 @@ pub mod fbsemantic { } } + #[inline] + #[allow(non_snake_case)] + pub fn consequent_as_poly_numeric_literal(&self) -> Option> { + if self.consequent_type() == Expression::PolyNumericLiteral { + self.consequent() + .map(|u| PolyNumericLiteral::init_from_table(u)) + } else { + None + } + } + #[inline] #[allow(non_snake_case)] pub fn consequent_as_integer_literal(&self) -> Option> { @@ -9127,6 +9283,16 @@ pub mod fbsemantic { } } + #[inline] + #[allow(non_snake_case)] + pub fn left_as_poly_numeric_literal(&self) -> Option> { + if self.left_type() == Expression::PolyNumericLiteral { + self.left().map(|u| PolyNumericLiteral::init_from_table(u)) + } else { + None + } + } + #[inline] #[allow(non_snake_case)] pub fn left_as_integer_literal(&self) -> Option> { @@ -9330,6 +9496,16 @@ pub mod fbsemantic { } } + #[inline] + #[allow(non_snake_case)] + pub fn right_as_poly_numeric_literal(&self) -> Option> { + if self.right_type() == Expression::PolyNumericLiteral { + self.right().map(|u| PolyNumericLiteral::init_from_table(u)) + } else { + None + } + } + #[inline] #[allow(non_snake_case)] pub fn right_as_integer_literal(&self) -> Option> { @@ -9713,6 +9889,17 @@ pub mod fbsemantic { } } + #[inline] + #[allow(non_snake_case)] + pub fn object_as_poly_numeric_literal(&self) -> Option> { + if self.object_type() == Expression::PolyNumericLiteral { + self.object() + .map(|u| PolyNumericLiteral::init_from_table(u)) + } else { + None + } + } + #[inline] #[allow(non_snake_case)] pub fn object_as_integer_literal(&self) -> Option> { @@ -10155,6 +10342,16 @@ pub mod fbsemantic { } } + #[inline] + #[allow(non_snake_case)] + pub fn array_as_poly_numeric_literal(&self) -> Option> { + if self.array_type() == Expression::PolyNumericLiteral { + self.array().map(|u| PolyNumericLiteral::init_from_table(u)) + } else { + None + } + } + #[inline] #[allow(non_snake_case)] pub fn array_as_integer_literal(&self) -> Option> { @@ -10358,6 +10555,16 @@ pub mod fbsemantic { } } + #[inline] + #[allow(non_snake_case)] + pub fn index_as_poly_numeric_literal(&self) -> Option> { + if self.index_type() == Expression::PolyNumericLiteral { + self.index().map(|u| PolyNumericLiteral::init_from_table(u)) + } else { + None + } + } + #[inline] #[allow(non_snake_case)] pub fn index_as_integer_literal(&self) -> Option> { @@ -11027,6 +11234,17 @@ pub mod fbsemantic { } } + #[inline] + #[allow(non_snake_case)] + pub fn argument_as_poly_numeric_literal(&self) -> Option> { + if self.argument_type() == Expression::PolyNumericLiteral { + self.argument() + .map(|u| PolyNumericLiteral::init_from_table(u)) + } else { + None + } + } + #[inline] #[allow(non_snake_case)] pub fn argument_as_integer_literal(&self) -> Option> { @@ -11438,6 +11656,16 @@ pub mod fbsemantic { } } + #[inline] + #[allow(non_snake_case)] + pub fn value_as_poly_numeric_literal(&self) -> Option> { + if self.value_type() == Expression::PolyNumericLiteral { + self.value().map(|u| PolyNumericLiteral::init_from_table(u)) + } else { + None + } + } + #[inline] #[allow(non_snake_case)] pub fn value_as_integer_literal(&self) -> Option> { @@ -12351,6 +12579,194 @@ pub mod fbsemantic { } } + pub enum PolyNumericLiteralOffset {} + #[derive(Copy, Clone, Debug, PartialEq)] + + pub struct PolyNumericLiteral<'a> { + pub _tab: flatbuffers::Table<'a>, + } + + impl<'a> flatbuffers::Follow<'a> for PolyNumericLiteral<'a> { + type Inner = PolyNumericLiteral<'a>; + #[inline] + fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table { buf: buf, loc: loc }, + } + } + } + + impl<'a> PolyNumericLiteral<'a> { + #[inline] + pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + PolyNumericLiteral { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, + args: &'args PolyNumericLiteralArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = PolyNumericLiteralBuilder::new(_fbb); + builder.add_value(args.value); + if let Some(x) = args.typ { + builder.add_typ(x); + } + if let Some(x) = args.loc { + builder.add_loc(x); + } + builder.add_typ_type(args.typ_type); + builder.finish() + } + + pub const VT_LOC: flatbuffers::VOffsetT = 4; + pub const VT_VALUE: flatbuffers::VOffsetT = 6; + pub const VT_TYP_TYPE: flatbuffers::VOffsetT = 8; + pub const VT_TYP: flatbuffers::VOffsetT = 10; + + #[inline] + pub fn loc(&self) -> Option> { + self._tab + .get::>>( + PolyNumericLiteral::VT_LOC, + None, + ) + } + #[inline] + pub fn value(&self) -> i64 { + self._tab + .get::(PolyNumericLiteral::VT_VALUE, Some(0)) + .unwrap() + } + #[inline] + pub fn typ_type(&self) -> MonoType { + self._tab + .get::(PolyNumericLiteral::VT_TYP_TYPE, Some(MonoType::NONE)) + .unwrap() + } + #[inline] + pub fn typ(&self) -> Option> { + self._tab + .get::>>( + PolyNumericLiteral::VT_TYP, + None, + ) + } + #[inline] + #[allow(non_snake_case)] + pub fn typ_as_basic(&self) -> Option> { + if self.typ_type() == MonoType::Basic { + self.typ().map(|u| Basic::init_from_table(u)) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn typ_as_var(&self) -> Option> { + if self.typ_type() == MonoType::Var { + self.typ().map(|u| Var::init_from_table(u)) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn typ_as_arr(&self) -> Option> { + if self.typ_type() == MonoType::Arr { + self.typ().map(|u| Arr::init_from_table(u)) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn typ_as_record(&self) -> Option> { + if self.typ_type() == MonoType::Record { + self.typ().map(|u| Record::init_from_table(u)) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn typ_as_fun(&self) -> Option> { + if self.typ_type() == MonoType::Fun { + self.typ().map(|u| Fun::init_from_table(u)) + } else { + None + } + } + } + + pub struct PolyNumericLiteralArgs<'a> { + pub loc: Option>>, + pub value: i64, + pub typ_type: MonoType, + pub typ: Option>, + } + impl<'a> Default for PolyNumericLiteralArgs<'a> { + #[inline] + fn default() -> Self { + PolyNumericLiteralArgs { + loc: None, + value: 0, + typ_type: MonoType::NONE, + typ: None, + } + } + } + pub struct PolyNumericLiteralBuilder<'a: 'b, 'b> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, + start_: flatbuffers::WIPOffset, + } + impl<'a: 'b, 'b> PolyNumericLiteralBuilder<'a, 'b> { + #[inline] + pub fn add_loc(&mut self, loc: flatbuffers::WIPOffset>) { + self.fbb_ + .push_slot_always::>( + PolyNumericLiteral::VT_LOC, + loc, + ); + } + #[inline] + pub fn add_value(&mut self, value: i64) { + self.fbb_ + .push_slot::(PolyNumericLiteral::VT_VALUE, value, 0); + } + #[inline] + pub fn add_typ_type(&mut self, typ_type: MonoType) { + self.fbb_.push_slot::( + PolyNumericLiteral::VT_TYP_TYPE, + typ_type, + MonoType::NONE, + ); + } + #[inline] + pub fn add_typ(&mut self, typ: flatbuffers::WIPOffset) { + self.fbb_ + .push_slot_always::>(PolyNumericLiteral::VT_TYP, typ); + } + #[inline] + pub fn new( + _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>, + ) -> PolyNumericLiteralBuilder<'a, 'b> { + let start = _fbb.start_table(); + PolyNumericLiteralBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } + } + pub enum IntegerLiteralOffset {} #[derive(Copy, Clone, Debug, PartialEq)] diff --git a/libflux/src/core/semantic/flatbuffers/tests.rs b/libflux/src/core/semantic/flatbuffers/tests.rs index 6691aa57ed..fad17fe7d7 100644 --- a/libflux/src/core/semantic/flatbuffers/tests.rs +++ b/libflux/src/core/semantic/flatbuffers/tests.rs @@ -356,10 +356,10 @@ fn compare_exprs( let fb_tbl = unwrap_or_fail("expr", fb_tbl)?; match (semantic_expr, fb_expr_ty) { ( - semantic::nodes::Expression::Integer(semantic_int), - fbsemantic::Expression::IntegerLiteral, + semantic::nodes::Expression::PolyNumeric(semantic_int), + fbsemantic::Expression::PolyNumericLiteral, ) => { - let fb_int = fbsemantic::IntegerLiteral::init_from_table(*fb_tbl); + let fb_int = fbsemantic::PolyNumericLiteral::init_from_table(*fb_tbl); compare_loc(&semantic_expr.loc(), &fb_int.loc())?; match semantic_int.value == fb_int.value() { true => Ok(()), diff --git a/libflux/src/core/semantic/flatbuffers/types.rs b/libflux/src/core/semantic/flatbuffers/types.rs index 2b2585621b..9fde079b93 100644 --- a/libflux/src/core/semantic/flatbuffers/types.rs +++ b/libflux/src/core/semantic/flatbuffers/types.rs @@ -83,6 +83,7 @@ impl From for Kind { fb::Kind::Subtractable => Kind::Subtractable, fb::Kind::Divisible => Kind::Divisible, fb::Kind::Numeric => Kind::Numeric, + fb::Kind::NumericDefaultInt => Kind::NumericDefaultInt, fb::Kind::Comparable => Kind::Comparable, fb::Kind::Equatable => Kind::Equatable, fb::Kind::Nullable => Kind::Nullable, @@ -100,6 +101,7 @@ impl From for fb::Kind { Kind::Subtractable => fb::Kind::Subtractable, Kind::Divisible => fb::Kind::Divisible, Kind::Numeric => fb::Kind::Numeric, + Kind::NumericDefaultInt => fb::Kind::NumericDefaultInt, Kind::Comparable => fb::Kind::Comparable, Kind::Equatable => fb::Kind::Equatable, Kind::Nullable => fb::Kind::Nullable, diff --git a/libflux/src/core/semantic/mod.rs b/libflux/src/core/semantic/mod.rs index 8779fcf1fb..17949e0343 100644 --- a/libflux/src/core/semantic/mod.rs +++ b/libflux/src/core/semantic/mod.rs @@ -78,5 +78,6 @@ pub fn convert_source(source: &str) -> Result { let mut sem_pkg = get_sem_pkg_from_source(source, &mut f)?; // TODO(affo): add a stdlib Importer. let (_, sub) = nodes::infer_pkg_types(&mut sem_pkg, Environment::empty(false), &mut f, &None)?; - Ok(nodes::inject_pkg_types(sem_pkg, &sub)) + let pkg = nodes::inject_pkg_types(sem_pkg, &sub); + Ok(pkg) } diff --git a/libflux/src/core/semantic/nodes.rs b/libflux/src/core/semantic/nodes.rs index 5b0bebfd70..70d8bfd0eb 100644 --- a/libflux/src/core/semantic/nodes.rs +++ b/libflux/src/core/semantic/nodes.rs @@ -133,7 +133,7 @@ pub enum Expression { Call(Box), Conditional(Box), StringExpr(Box), - + PolyNumeric(PolyNumericLit), Integer(IntegerLit), Float(FloatLit), StringLit(StringLit), @@ -159,6 +159,7 @@ impl Expression { Expression::Call(e) => e.typ.clone(), Expression::Conditional(e) => e.alternate.type_of(), Expression::StringExpr(_) => MonoType::String, + Expression::PolyNumeric(e) => e.typ.clone(), Expression::Integer(_) => MonoType::Int, Expression::Float(_) => MonoType::Float, Expression::StringLit(_) => MonoType::String, @@ -183,6 +184,7 @@ impl Expression { Expression::Call(e) => &e.loc, Expression::Conditional(e) => &e.loc, Expression::StringExpr(e) => &e.loc, + Expression::PolyNumeric(lit) => &lit.loc, Expression::Integer(lit) => &lit.loc, Expression::Float(lit) => &lit.loc, Expression::StringLit(lit) => &lit.loc, @@ -207,6 +209,7 @@ impl Expression { Expression::Call(e) => e.infer(env, f), Expression::Conditional(e) => e.infer(env, f), Expression::StringExpr(e) => e.infer(env, f), + Expression::PolyNumeric(lit) => lit.infer(env), Expression::Integer(lit) => lit.infer(env), Expression::Float(lit) => lit.infer(env), Expression::StringLit(lit) => lit.infer(env), @@ -231,6 +234,7 @@ impl Expression { Expression::Call(e) => Expression::Call(Box::new(e.apply(&sub))), Expression::Conditional(e) => Expression::Conditional(Box::new(e.apply(&sub))), Expression::StringExpr(e) => Expression::StringExpr(Box::new(e.apply(&sub))), + Expression::PolyNumeric(lit) => Expression::PolyNumeric(lit.apply(&sub)), Expression::Integer(lit) => Expression::Integer(lit.apply(&sub)), Expression::Float(lit) => Expression::Float(lit.apply(&sub)), Expression::StringLit(lit) => Expression::StringLit(lit.apply(&sub)), @@ -254,7 +258,9 @@ where T: Importer, { let (env, cons) = pkg.infer(env, f, importer)?; - Ok((env, infer::solve(&cons, &mut TvarKinds::new(), f)?)) + let sub = infer::solve(&cons, &mut TvarKinds::new(), f)?; + + Ok((env, sub)) } pub fn infer_file(file: &mut File, env: Environment, f: &mut Fresher, importer: &T) -> Result @@ -543,10 +549,8 @@ impl VariableAssgn { let mut kinds = TvarKinds::new(); let sub = infer::solve(&constraints, &mut kinds, f)?; - // Apply substitution to the type environment let mut env = env.apply(&sub); - let t = self.init.type_of().apply(&sub); let p = infer::generalize(&env, &kinds, t); @@ -555,6 +559,7 @@ impl VariableAssgn { // // Note these variables are fixed after generalization // and so it is safe to update these nodes in place. + self.vars = p.vars.clone(); self.cons = p.cons.clone(); @@ -1642,6 +1647,31 @@ impl BooleanLit { } } +#[derive(Derivative)] +#[derivative(Debug, PartialEq, Clone)] +pub struct PolyNumericLit { + pub loc: ast::SourceLocation, + pub value: i64, + #[derivative(PartialEq = "ignore")] + pub typ: MonoType, +} + +impl PolyNumericLit { + fn infer(&self, env: Environment) -> Result { + let cons = Constraints::from(vec![Constraint::Kind { + act: self.typ.clone(), + exp: Kind::NumericDefaultInt, + loc: self.loc.clone(), + }]); + Ok((env, cons)) + } + fn apply(mut self, sub: &Substitution) -> Self { + self.typ = self.typ.apply(&sub); + self + } +} + +// No Longer in Use #[derive(Derivative)] #[derivative(Debug, PartialEq, Clone)] pub struct IntegerLit { diff --git a/libflux/src/core/semantic/sub.rs b/libflux/src/core/semantic/sub.rs index 300f9e2730..03c2d7e193 100644 --- a/libflux/src/core/semantic/sub.rs +++ b/libflux/src/core/semantic/sub.rs @@ -1,5 +1,4 @@ use crate::semantic::types::{MonoType, SubstitutionMap, Tvar}; - // A substitution defines a function that takes a monotype as input // and returns a monotype as output. The output type is interpreted // as being equivalent to the input type. @@ -34,6 +33,10 @@ impl Substitution { Substitution(SubstitutionMap::new()) } + pub fn insert(&mut self, k: Tvar, v: MonoType) { + self.0.insert(k, v); + } + pub fn apply(&self, tv: Tvar) -> MonoType { match self.0.get(&tv) { Some(t) => t.clone(), diff --git a/libflux/src/core/semantic/tests.rs b/libflux/src/core/semantic/tests.rs index 389af2ea8e..1986f5b75d 100644 --- a/libflux/src/core/semantic/tests.rs +++ b/libflux/src/core/semantic/tests.rs @@ -23,20 +23,20 @@ //! use std::collections::HashMap; +use crate::ast; +use crate::ast::get_err_type_expression; +use crate::parser; +use crate::parser::parse_string; use crate::semantic::bootstrap::build_polytype; +use crate::semantic::convert::convert_polytype; use crate::semantic::convert::convert_with; use crate::semantic::env::Environment; use crate::semantic::fresh::Fresher; use crate::semantic::import::Importer; use crate::semantic::nodes; +//use crate::semantic::sub::{Substitutable, Substitution}; use crate::semantic::types::{MaxTvar, MonoType, PolyType, PolyTypeMap, SemanticMap, TvarKinds}; -use crate::ast; -use crate::ast::get_err_type_expression; -use crate::parser; -use crate::parser::parse_string; -use crate::semantic::convert::convert_polytype; - use colored::*; fn parse_program(src: &str) -> ast::Package { @@ -57,7 +57,6 @@ fn parse_map(m: HashMap<&str, &str>) -> PolyTypeMap { let typ_expr = p.parse_type_expression(); let err = get_err_type_expression(typ_expr.clone()); - if err != "" { let msg = format!("TypeExpression parsing failed for {}. {:?}", name, err); panic!(msg) @@ -125,7 +124,6 @@ fn infer_types( let mut f = Fresher::from(max.0 + 1); let pkg = parse_program(src); - let got = match nodes::infer_pkg_types( &mut convert_with(pkg, &mut f).expect("analysis failed"), Environment::new(env), @@ -153,6 +151,7 @@ fn infer_types( ); } } + return Ok(got.into()); } @@ -191,6 +190,7 @@ fn infer_types( /// "f" => "(x: A) => A", /// ], /// ], + /// src: r#" /// import foo "path/to/foo" /// @@ -205,16 +205,19 @@ fn infer_types( /// macro_rules! test_infer { ($(env: $env:expr,)? $(imp: $imp:expr,)? src: $src:expr, exp: $exp:expr $(,)? ) => {{ + #[allow(unused_mut, unused_assignments)] let mut env = HashMap::new(); $( env = $env; + )? #[allow(unused_mut, unused_assignments)] let mut imp = HashMap::new(); $( imp = $imp; )? + if let Err(e) = infer_types($src, env, imp, Some($exp)) { panic!(format!("{}", e)); } @@ -440,7 +443,7 @@ fn literals() { "#, exp: map![ "a" => "string", - "b" => "int", + "b" => "A where A: NumericDefaultInt", "c" => "float", "d" => "duration", "e" => "time", @@ -561,7 +564,7 @@ fn array_lit() { test_infer! { src: "a = [1, 2, 3]", exp: map![ - "a" => "[int]", + "a" => "[A] where A: NumericDefaultInt", ], } test_infer! { @@ -617,11 +620,14 @@ fn array_lit() { test_infer! { src: "a = [{a:0, b:0.0}, {a:1, b:1.1}]", exp: map![ - "a" => "[{a: int , b: float}]", + "a" => "[{a: A , b: float}] where A: NumericDefaultInt", ], } - test_infer_err! { + test_infer! { src: "a = [1, 1.1]", + exp: map![ + "a" => "[float]", + ], } } #[test] @@ -748,6 +754,7 @@ fn binary_expr_addition() { test_infer! { env: map![ "a" => "float", + "b" => "float", ], src: r#" @@ -2710,7 +2717,7 @@ fn call_expr() { f(x: (w=<-) => w) "#, exp: map![ - "f" => "(x:(<-:int) => C) => C", + "f" => "(x:(<-:A) => C) => C where A: NumericDefaultInt", ] } // pipe args have different names @@ -2737,8 +2744,8 @@ fn call_expr() { g = () => f(arg: (x) => 5 + x) "#, exp: map![ - "f" => "(?arg:(<-x:int) => int) => int", - "g" => "() => int", + "f" => "(?arg:(<-x:A) => A) => A where A: NumericDefaultInt", + "g" => "() => A where A: NumericDefaultInt, A: Addable", ] } } @@ -2762,19 +2769,68 @@ fn polymorphic_instantiation() { exp: map![ "f" => "(x: A) => A", - "a" => "int", + "a" => "A where A:NumericDefaultInt", "b" => "float", "c" => "string", "d" => "duration", "e" => "time", "g" => "regexp", - "h" => "[int]", + "h" => "[A] where A:NumericDefaultInt", "i" => "[A]", - "j" => "{a: int , b: float}", + "j" => "{a: A , b: float} where A: NumericDefaultInt", ], } } #[test] +fn polymorphic_numeric_literals() { + test_infer! { + src: r#" + a = 1 + b = 1.1 + c = a + b + "#, + exp: map![ + "a" => "A where A: NumericDefaultInt", + "b" => "float", + "c" => "float", + ], + } + test_infer! { + src: r#" + f = (x) => x + 1.1 + a = f(x: 100) + "#, + exp: map![ + "f" => "(x: float) => float", + "a" => "float", + ], + } + test_infer! { + src: r#" + f = (x, y) => x + y + z = 1 + a = f(x: z, y: 1) + b = f(x: z, y: 0.5) + + "#, + exp: map![ + "f" => "(x: A, y: A) => A where A: Addable", + "z" => "A where A: NumericDefaultInt", + "a" => "A where A: NumericDefaultInt, A: Addable", + "b" => "float" + + ], + } + test_infer_err! { + src: r#" + f = (x,y) => x + y + a = 1 + b = "hello world" + c = f(a, b) + "#, + } +} +#[test] fn constrain_tvars() { test_infer! { src: r#" @@ -2782,8 +2838,8 @@ fn constrain_tvars() { a = f(x: 100) "#, exp: map![ - "f" => "(x: int) => int", - "a" => "int", + "f" => "(x: A) => A where A: NumericDefaultInt, A: Addable", + "a" => "A where A: NumericDefaultInt, A: Addable", ], } test_infer_err! { @@ -2819,8 +2875,8 @@ fn constrained_generics_addable() { c = f(a: "0", b: "1") "#, exp: map![ - "f" => "(a: A, b: A) => A where A: Addable ", - "a" => "int", + "f" => "(a: A, b: A) => A where A: Addable", + "a" => "A where A: NumericDefaultInt, A: Addable", "b" => "float", "c" => "string", ], @@ -2828,7 +2884,7 @@ fn constrained_generics_addable() { test_infer_err! { src: r#" f = (a, b) => a + b - f(a: 100, b: 0.1) + f(a: 100, b: "0.1") "#, } test_infer_err! { @@ -2877,8 +2933,8 @@ fn constrained_generics_subtractable() { b = f(a: 0.1, b: 0.2) "#, exp: map![ - "f" => "(a: A, b: A) => A where A: Subtractable ", - "a" => "int", + "f" => "(a: A, b: A) => A where A: Subtractable", + "a" => "A where A:NumericDefaultInt, A: Subtractable", "b" => "float", ], } @@ -2928,8 +2984,8 @@ fn constrained_generics_divisible() { b = f(a: 0.1, b: 0.2) "#, exp: map![ - "f" => "(a: A, b: A) => A where A: Divisible ", - "a" => "int", + "f" => "(a: A, b: A) => A where A: Divisible", + "a" => "A where A: NumericDefaultInt, A: Divisible", "b" => "float", ], } @@ -3170,16 +3226,16 @@ fn function_instantiation_and_generalization() { c = f(x: (b) => ({b: b})) "#, exp: map![ - "r" => "int", - "x" => "int", - "s" => "{list: [int]}", - "y" => "[int]", - "t" => "{list: [int]}", - "z" => "[int]", - "f" => "(x: (b: [int]) => A) => A", - "a" => "[int]", - "b" => "int", - "c" => "{b: [int]}", + "r" => "A where A: NumericDefaultInt", + "x" => "A where A: NumericDefaultInt", + "s" => "{list: [A]} where A: NumericDefaultInt", + "y" => "[A] where A: NumericDefaultInt", + "t" => "{list: [A]} where A: NumericDefaultInt", + "z" => "[A] where A: NumericDefaultInt", + "f" => "(x: (b: [A]) => B) => B where A: NumericDefaultInt", + "a" => "[A] where A: NumericDefaultInt", + "b" => "A where A: NumericDefaultInt", + "c" => "{b: [A]} where A: NumericDefaultInt", ], } test_infer_err! { @@ -3214,9 +3270,9 @@ fn function_default_arguments_1() { y = f(a: x, b: f(a:x)) "#, exp: map![ - "f" => "(a: int, ?b: int) => int", - "x" => "int", - "y" => "int", + "f" => "(a: A, ?b: A) => A where A: NumericDefaultInt, A: Addable", + "x" => "A where A: NumericDefaultInt, A: Addable", + "y" => "A where A: NumericDefaultInt, A: Addable", ], } } @@ -3231,11 +3287,11 @@ fn function_default_arguments_2() { z = f(a: 3.3, b: 3) "#, exp: map![ - "f" => "(a: float, b: int, ?c: float, ?d: int) => {r: float , s: int}", - "w" => "{r: float , s: int}", - "x" => "{r: float , s: int}", - "y" => "{r: float , s: int}", - "z" => "{r: float , s: int}", + "f" => "(a: float, b: A, ?c: float, ?d: A) => {r: float , s: A} where A: NumericDefaultInt, A: Addable", + "w" => "{r: float , s: A} where A: NumericDefaultInt, A: Addable", + "x" => "{r: float , s: A} where A: NumericDefaultInt, A: Addable", + "y" => "{r: float , s: A} where A: NumericDefaultInt, A: Addable", + "z" => "{r: float , s: A} where A: NumericDefaultInt, A: Addable", ], } } @@ -3250,7 +3306,7 @@ fn function_pipe_identity() { exp: map![ "f" => "(<-a: A) => A", "x" => "float", - "y" => "int", + "y" => "A where A: NumericDefaultInt", ], } } @@ -3266,9 +3322,9 @@ fn function_default_arguments_and_pipes() { "#, exp: map![ "f" => "(<-t: B, f: (<-: B, a: A) => C, g: A) => C", - "x" => "(a: int, ?b: int, <-m: int) => int", + "x" => "(a: A, ?b: A, <-m: A) => A where A: NumericDefaultInt, A: Addable", "z" => "(a: {B with m: A}, ?b: float, ?c: float, <-m: float) => {r: A , s: float}", - "y" => "int", + "y" => "A where A: NumericDefaultInt, A: Addable", "v" => "{s: float, r: string}", ], } @@ -3324,7 +3380,7 @@ fn test_error_messages() { 1 + "1" "#, // Location points to right expression expression - err: "type error @2:17-2:20: expected int but found string", + err: "type error @2:17-2:20: string is not NumericDefaultInt", } test_error_msg! { src: r#" @@ -3347,28 +3403,28 @@ fn test_error_messages() { "Hey ${bob} it's me ${joe}!" "#, // Location points to second interpolated expression - err: "type error @4:35-4:38: expected string but found int", + err: "type error @4:35-4:38: string is not NumericDefaultInt", } test_error_msg! { src: r#" if 0 then "a" else "b" "#, // Location points to if expression - err: "type error @2:16-2:17: expected bool but found int", + err: "type error @2:16-2:17: bool is not NumericDefaultInt", } test_error_msg! { src: r#" if exists 0 then 0 else "b" "#, // Location points to else expression - err: "type error @2:37-2:40: expected int but found string", + err: "type error @2:37-2:40: string is not NumericDefaultInt", } test_error_msg! { src: r#" [1, "2"] "#, // Location points to second element of array - err: "type error @2:17-2:20: expected int but found string", + err: "type error @2:17-2:20: string is not NumericDefaultInt", } test_error_msg! { src: r#" @@ -3381,10 +3437,10 @@ fn test_error_messages() { test_error_msg! { src: r#" a = [1, 2, 3] - a[1] + 1.1 + a[1] + "1.1" "#, // Location points to right expression - err: "type error @3:20-3:23: expected int but found float", + err: "type error @3:20-3:25: string is not NumericDefaultInt", } test_error_msg! { src: r#" @@ -3392,7 +3448,7 @@ fn test_error_messages() { a[1] "#, // Location points to the identifier a - err: "type error @3:13-3:14: expected [A] but found int", + err: "type error @3:13-3:14: [A] is not NumericDefaultInt", } test_error_msg! { src: r#" @@ -3400,7 +3456,7 @@ fn test_error_messages() { a.x "#, // Location points to the identifier a - err: "type error @3:13-3:14: expected {A with x:B} but found [int]", + err: "type error @3:13-3:14: expected {A with x:B} but found [C]", } test_error_msg! { src: r#" diff --git a/libflux/src/core/semantic/types.rs b/libflux/src/core/semantic/types.rs index 11e7dd2c03..932b42fe5a 100644 --- a/libflux/src/core/semantic/types.rs +++ b/libflux/src/core/semantic/types.rs @@ -18,7 +18,6 @@ pub struct PolyType { pub cons: TvarKinds, pub expr: MonoType, } - pub type PolyTypeMap = SemanticMap; pub type PolyTypeMapMap = SemanticMap>; @@ -220,6 +219,7 @@ pub enum Kind { Record, Negatable, Timeable, + NumericDefaultInt, } impl fmt::Display for Kind { @@ -229,6 +229,7 @@ impl fmt::Display for Kind { Kind::Subtractable => f.write_str("Subtractable"), Kind::Divisible => f.write_str("Divisible"), Kind::Numeric => f.write_str("Numeric"), + Kind::NumericDefaultInt => f.write_str("NumericDefaultInt"), Kind::Comparable => f.write_str("Comparable"), Kind::Equatable => f.write_str("Equatable"), Kind::Nullable => f.write_str("Nullable"), @@ -399,6 +400,7 @@ impl MonoType { | Kind::Subtractable | Kind::Divisible | Kind::Numeric + | Kind::NumericDefaultInt | Kind::Comparable | Kind::Equatable | Kind::Nullable @@ -413,6 +415,7 @@ impl MonoType { | Kind::Subtractable | Kind::Divisible | Kind::Numeric + | Kind::NumericDefaultInt | Kind::Comparable | Kind::Equatable | Kind::Nullable @@ -427,6 +430,7 @@ impl MonoType { | Kind::Subtractable | Kind::Divisible | Kind::Numeric + | Kind::NumericDefaultInt | Kind::Comparable | Kind::Equatable | Kind::Nullable @@ -1325,6 +1329,12 @@ mod tests { fn display_kind_numeric() { assert!(Kind::Numeric.to_string() == "Numeric"); } + + #[test] + fn display_kind_numeric_default_int() { + assert!(Kind::NumericDefaultInt.to_string() == "NumericDefaultInt"); + } + #[test] fn display_kind_comparable() { assert!(Kind::Comparable.to_string() == "Comparable"); diff --git a/libflux/src/core/semantic/walk/_walk.rs b/libflux/src/core/semantic/walk/_walk.rs index 43fa5e5943..419554b29c 100644 --- a/libflux/src/core/semantic/walk/_walk.rs +++ b/libflux/src/core/semantic/walk/_walk.rs @@ -29,6 +29,7 @@ pub enum Node<'a> { CallExpr(&'a CallExpr), ConditionalExpr(&'a ConditionalExpr), StringExpr(&'a StringExpr), + PolyNumericLit(&'a PolyNumericLit), IntegerLit(&'a IntegerLit), FloatLit(&'a FloatLit), StringLit(&'a StringLit), @@ -75,6 +76,7 @@ impl<'a> fmt::Display for Node<'a> { Node::CallExpr(_) => write!(f, "CallExpr"), Node::ConditionalExpr(_) => write!(f, "ConditionalExpr"), Node::StringExpr(_) => write!(f, "StringExpr"), + Node::PolyNumericLit(_) => write!(f, "PolyNumericLit"), Node::IntegerLit(_) => write!(f, "IntegerLit"), Node::FloatLit(_) => write!(f, "FloatLit"), Node::StringLit(_) => write!(f, "StringLit"), @@ -123,6 +125,7 @@ impl<'a> Node<'a> { Node::CallExpr(n) => &n.loc, Node::ConditionalExpr(n) => &n.loc, Node::StringExpr(n) => &n.loc, + Node::PolyNumericLit(n) => &n.loc, Node::IntegerLit(n) => &n.loc, Node::FloatLit(n) => &n.loc, Node::StringLit(n) => &n.loc, @@ -160,6 +163,7 @@ impl<'a> Node<'a> { Some(Expression::Conditional(Box::new((*n).clone())).type_of()) } Node::StringExpr(n) => Some(Expression::StringExpr(Box::new((*n).clone())).type_of()), + Node::PolyNumericLit(n) => Some(Expression::PolyNumeric((*n).clone()).type_of()), Node::IntegerLit(n) => Some(Expression::Integer((*n).clone()).type_of()), Node::FloatLit(n) => Some(Expression::Float((*n).clone()).type_of()), Node::StringLit(n) => Some(Expression::StringLit((*n).clone()).type_of()), @@ -189,6 +193,7 @@ impl<'a> Node<'a> { Expression::Call(ref e) => Node::CallExpr(e), Expression::Conditional(ref e) => Node::ConditionalExpr(e), Expression::StringExpr(ref e) => Node::StringExpr(e), + Expression::PolyNumeric(ref e) => Node::PolyNumericLit(e), Expression::Integer(ref e) => Node::IntegerLit(e), Expression::Float(ref e) => Node::FloatLit(e), Expression::StringLit(ref e) => Node::StringLit(e), @@ -405,6 +410,7 @@ where walk(v, Rc::new(Node::from_string_expr_part(part))); } } + Node::PolyNumericLit(_) => {} Node::IntegerLit(_) => {} Node::FloatLit(_) => {} Node::StringLit(_) => {} @@ -518,9 +524,9 @@ mod tests { "File", "ExprStmt", "ArrayExpr", - "IntegerLit", - "IntegerLit", - "IntegerLit", + "PolyNumericLit", + "PolyNumericLit", + "PolyNumericLit", ], ) } @@ -534,7 +540,7 @@ mod tests { "FunctionExpr", "Block::Return", "ReturnStmt", - "IntegerLit", + "PolyNumericLit", ], ) } @@ -554,13 +560,13 @@ mod tests { "Block::Variable", "VariableAssgn", "Identifier", - "IntegerLit", + "PolyNumericLit", "Block::Variable", "VariableAssgn", "Identifier", "BinaryExpr", - "IntegerLit", - "IntegerLit", + "PolyNumericLit", + "PolyNumericLit", "Block::Expr", "ExprStmt", "BinaryExpr", @@ -582,7 +588,7 @@ mod tests { "FunctionExpr", "FunctionParameter", "Identifier", - "IntegerLit", + "PolyNumericLit", "Block::Return", "ReturnStmt", "IdentifierExpr", @@ -612,7 +618,7 @@ mod tests { "ObjectExpr", "Property", "Identifier", - "IntegerLit", + "PolyNumericLit", "Property", "Identifier", "IdentifierExpr", @@ -683,7 +689,7 @@ mod tests { "IdentifierExpr", "Property", "Identifier", - "IntegerLit", + "PolyNumericLit", ], ) } @@ -730,7 +736,7 @@ mod tests { } #[test] fn test_integer_lit() { - test_walk("1", vec!["File", "ExprStmt", "IntegerLit"]) + test_walk("1", vec!["File", "ExprStmt", "PolyNumericLit"]) } #[test] fn test_float_lit() { @@ -797,7 +803,7 @@ mod tests { "FunctionExpr", "Block::Return", "ReturnStmt", - "IntegerLit", + "PolyNumericLit", ], ) } @@ -810,7 +816,7 @@ mod tests { "TestStmt", "VariableAssgn", "Identifier", - "IntegerLit", + "PolyNumericLit", ], ) } diff --git a/libflux/src/core/semantic/walk/walk_mut.rs b/libflux/src/core/semantic/walk/walk_mut.rs index 0f0109309f..360ca7f93f 100644 --- a/libflux/src/core/semantic/walk/walk_mut.rs +++ b/libflux/src/core/semantic/walk/walk_mut.rs @@ -29,6 +29,7 @@ pub enum NodeMut<'a> { CallExpr(&'a mut CallExpr), ConditionalExpr(&'a mut ConditionalExpr), StringExpr(&'a mut StringExpr), + PolyNumericLit(&'a mut PolyNumericLit), IntegerLit(&'a mut IntegerLit), FloatLit(&'a mut FloatLit), StringLit(&'a mut StringLit), @@ -75,6 +76,7 @@ impl<'a> fmt::Display for NodeMut<'a> { NodeMut::CallExpr(_) => write!(f, "CallExpr"), NodeMut::ConditionalExpr(_) => write!(f, "ConditionalExpr"), NodeMut::StringExpr(_) => write!(f, "StringExpr"), + NodeMut::PolyNumericLit(_) => write!(f, "PolyNumericLit"), NodeMut::IntegerLit(_) => write!(f, "IntegerLit"), NodeMut::FloatLit(_) => write!(f, "FloatLit"), NodeMut::StringLit(_) => write!(f, "StringLit"), @@ -122,6 +124,7 @@ impl<'a> NodeMut<'a> { NodeMut::CallExpr(n) => &n.loc, NodeMut::ConditionalExpr(n) => &n.loc, NodeMut::StringExpr(n) => &n.loc, + NodeMut::PolyNumericLit(n) => &n.loc, NodeMut::IntegerLit(n) => &n.loc, NodeMut::FloatLit(n) => &n.loc, NodeMut::StringLit(n) => &n.loc, @@ -163,6 +166,7 @@ impl<'a> NodeMut<'a> { NodeMut::StringExpr(n) => { Some(Expression::StringExpr(Box::new((*n).clone())).type_of()) } + NodeMut::PolyNumericLit(n) => Some(Expression::PolyNumeric((*n).clone()).type_of()), NodeMut::IntegerLit(n) => Some(Expression::Integer((*n).clone()).type_of()), NodeMut::FloatLit(n) => Some(Expression::Float((*n).clone()).type_of()), NodeMut::StringLit(n) => Some(Expression::StringLit((*n).clone()).type_of()), @@ -194,6 +198,7 @@ impl<'a> NodeMut<'a> { NodeMut::CallExpr(ref mut n) => n.loc = loc, NodeMut::ConditionalExpr(ref mut n) => n.loc = loc, NodeMut::StringExpr(ref mut n) => n.loc = loc, + NodeMut::PolyNumericLit(ref mut n) => n.loc = loc, NodeMut::IntegerLit(ref mut n) => n.loc = loc, NodeMut::FloatLit(ref mut n) => n.loc = loc, NodeMut::StringLit(ref mut n) => n.loc = loc, @@ -233,6 +238,7 @@ impl<'a> NodeMut<'a> { Expression::Call(ref mut e) => NodeMut::CallExpr(e), Expression::Conditional(ref mut e) => NodeMut::ConditionalExpr(e), Expression::StringExpr(ref mut e) => NodeMut::StringExpr(e), + Expression::PolyNumeric(ref mut e) => NodeMut::PolyNumericLit(e), Expression::Integer(ref mut e) => NodeMut::IntegerLit(e), Expression::Float(ref mut e) => NodeMut::FloatLit(e), Expression::StringLit(ref mut e) => NodeMut::StringLit(e), @@ -412,6 +418,7 @@ where walk_mut(v, &mut NodeMut::from_string_expr_part(&mut part)); } } + NodeMut::PolyNumericLit(_) => {} NodeMut::IntegerLit(_) => {} NodeMut::FloatLit(_) => {} NodeMut::StringLit(_) => {} @@ -526,9 +533,9 @@ mod tests { "File", "ExprStmt", "ArrayExpr", - "IntegerLit", - "IntegerLit", - "IntegerLit", + "PolyNumericLit", + "PolyNumericLit", + "PolyNumericLit", ], ) } @@ -542,7 +549,7 @@ mod tests { "FunctionExpr", "Block::Return", "ReturnStmt", - "IntegerLit", + "PolyNumericLit", ], ) } @@ -562,13 +569,13 @@ mod tests { "Block::Variable", "VariableAssgn", "Identifier", - "IntegerLit", + "PolyNumericLit", "Block::Variable", "VariableAssgn", "Identifier", "BinaryExpr", - "IntegerLit", - "IntegerLit", + "PolyNumericLit", + "PolyNumericLit", "Block::Expr", "ExprStmt", "BinaryExpr", @@ -590,7 +597,7 @@ mod tests { "FunctionExpr", "FunctionParameter", "Identifier", - "IntegerLit", + "PolyNumericLit", "Block::Return", "ReturnStmt", "IdentifierExpr", @@ -620,7 +627,7 @@ mod tests { "ObjectExpr", "Property", "Identifier", - "IntegerLit", + "PolyNumericLit", "Property", "Identifier", "IdentifierExpr", @@ -691,7 +698,7 @@ mod tests { "IdentifierExpr", "Property", "Identifier", - "IntegerLit", + "PolyNumericLit", ], ) } @@ -738,7 +745,7 @@ mod tests { } #[test] fn test_integer_lit() { - test_walk("1", vec!["File", "ExprStmt", "IntegerLit"]) + test_walk("1", vec!["File", "ExprStmt", "PolyNumericLit"]) } #[test] fn test_float_lit() { @@ -805,7 +812,7 @@ mod tests { "FunctionExpr", "Block::Return", "ReturnStmt", - "IntegerLit", + "PolyNumericLit", ], ) } @@ -818,7 +825,7 @@ mod tests { "TestStmt", "VariableAssgn", "Identifier", - "IntegerLit", + "PolyNumericLit", ], ) } @@ -922,6 +929,9 @@ mod tests { NodeMut::StringExpr(n) => { Some(Expression::StringExpr(Box::new((*n).clone())).type_of()) } + NodeMut::PolyNumericLit(n) => { + Some(Expression::PolyNumeric((*n).clone()).type_of()) + } NodeMut::IntegerLit(n) => Some(Expression::Integer((*n).clone()).type_of()), NodeMut::FloatLit(n) => Some(Expression::Float((*n).clone()).type_of()), NodeMut::StringLit(n) => Some(Expression::StringLit((*n).clone()).type_of()), diff --git a/libflux/src/core/tests/analyze_test.rs b/libflux/src/core/tests/analyze_test.rs index 9f6c470247..dbecd59dcb 100644 --- a/libflux/src/core/tests/analyze_test.rs +++ b/libflux/src/core/tests/analyze_test.rs @@ -55,9 +55,10 @@ f(a: s) loc: ast::BaseNode::default().location, name: "n".to_string(), }, - Expression::Integer(IntegerLit { + Expression::PolyNumeric(PolyNumericLit { loc: ast::BaseNode::default().location, value: 1, + typ: MonoType::Var(Tvar(0)), }), ast::BaseNode::default().location, ))), diff --git a/libflux/src/flux/lib.rs b/libflux/src/flux/lib.rs index 99cc656e5f..b9229456a9 100644 --- a/libflux/src/flux/lib.rs +++ b/libflux/src/flux/lib.rs @@ -430,7 +430,8 @@ impl SemanticAnalyzer { } } self.env = env; - Ok(inject_pkg_types(sem_pkg, &sub)) + let pkg = inject_pkg_types(sem_pkg, &sub); + Ok(pkg) } } @@ -490,7 +491,8 @@ pub unsafe extern "C" fn flux_analyze_with( /// and prelude. pub fn analyze(ast_pkg: ast::Package) -> Result { let (sem_pkg, _, sub) = infer_with_env(ast_pkg, fresher(), None)?; - Ok(inject_pkg_types(sem_pkg, &sub)) + let pkg = inject_pkg_types(sem_pkg, &sub); + Ok(pkg) } /// infer_with_env consumes the given AST package, inject the type bindings from the given @@ -697,7 +699,7 @@ vstr = v.str + "hello" let mut t = find_var_type(pkg, "v".into()).expect("Should be able to get a MonoType."); let mut v = MonoTypeNormalizer::new(); v.normalize(&mut t); - assert_eq!(format!("{}", t), "{B with int:int, sweet:A, str:string}"); + assert_eq!(format!("{}", t), "{C with int:A, sweet:B, str:string}"); assert_eq!( serde_json::to_string_pretty(&t).unwrap(), @@ -706,7 +708,9 @@ vstr = v.str + "hello" "type": "Extension", "head": { "k": "int", - "v": "Int" + "v": { + "Var": 0 + } }, "tail": { "Record": { @@ -714,7 +718,7 @@ vstr = v.str + "hello" "head": { "k": "sweet", "v": { - "Var": 0 + "Var": 1 } }, "tail": { @@ -725,7 +729,7 @@ vstr = v.str + "hello" "v": "String" }, "tail": { - "Var": 1 + "Var": 2 } } } @@ -744,9 +748,12 @@ vint = v + 2 let mut p = Parser::new(&source); let pkg: ast::Package = p.parse_file("".to_string()).into(); let t = find_var_type(pkg, "v".into()).expect("Should be able to get a MonoType."); - assert_eq!(t, MonoType::Int); + assert_eq!(t, MonoType::Var(Tvar(8291))); - assert_eq!(serde_json::to_string_pretty(&t).unwrap(), "\"Int\""); + assert_eq!( + serde_json::to_string_pretty(&t).unwrap(), + "{\n \"Var\": 8291\n}" + ); } #[test] @@ -761,7 +768,7 @@ p = o.ethan let mut t = find_var_type(pkg, "v".into()).expect("Should be able to get a MonoType."); let mut v = MonoTypeNormalizer::new(); v.normalize(&mut t); - assert_eq!(format!("{}", t), "{B with int:int, ethan:A}"); + assert_eq!(format!("{}", t), "{C with int:A, ethan:B}"); assert_eq!( serde_json::to_string_pretty(&t).unwrap(), @@ -770,7 +777,9 @@ p = o.ethan "type": "Extension", "head": { "k": "int", - "v": "Int" + "v": { + "Var": 0 + } }, "tail": { "Record": { @@ -778,11 +787,11 @@ p = o.ethan "head": { "k": "ethan", "v": { - "Var": 0 + "Var": 1 } }, "tail": { - "Var": 1 + "Var": 2 } } } diff --git a/plan/logical_test.go b/plan/logical_test.go index 1d2cf710da..d884890215 100644 --- a/plan/logical_test.go +++ b/plan/logical_test.go @@ -552,11 +552,11 @@ func TestLogicalPlanner(t *testing.T) { Left: &semantic.BinaryExpression{ Operator: ast.LessThanOperator, Left: &semantic.MemberExpression{Object: &semantic.IdentifierExpression{Name: "r"}, Property: "_value"}, - Right: &semantic.IntegerLiteral{Value: 100}}, + Right: &semantic.PolyNumericLiteral{Value: 100}}, Right: &semantic.BinaryExpression{ Operator: ast.NotEqualOperator, Left: &semantic.MemberExpression{Object: &semantic.IdentifierExpression{Name: "r"}, Property: "_value"}, - Right: &semantic.IntegerLiteral{}}}, + Right: &semantic.PolyNumericLiteral{}}}, }, }, }, @@ -575,7 +575,7 @@ func TestLogicalPlanner(t *testing.T) { Value: &semantic.BinaryExpression{ Operator: ast.MultiplicationOperator, Left: &semantic.MemberExpression{Object: &semantic.IdentifierExpression{Name: "r"}, Property: "_value"}, - Right: &semantic.IntegerLiteral{Value: 10}}}}}, + Right: &semantic.PolyNumericLiteral{Value: 10}}}}}, }, }}}}}), plan.CreateLogicalNode("yield4", &universe.YieldProcedureSpec{Name: "result"}), diff --git a/runtime/analyze_libflux_test.go b/runtime/analyze_libflux_test.go index ac0258aa96..a9d1155d23 100644 --- a/runtime/analyze_libflux_test.go +++ b/runtime/analyze_libflux_test.go @@ -20,7 +20,7 @@ func TestAnalyzeSource(t *testing.T) { { name: "failure", flx: `x = 10 + "foo"`, - err: errors.New("type error @1:10-1:15: expected int but found string"), + err: errors.New("type error @1:10-1:15: string is not NumericDefaultInt"), }, } for _, tc := range tcs { diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go index 28a13d57ee..0d2a70a98d 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -25,7 +25,7 @@ func TestEval(t *testing.T) { t.Fatal(err) } want := map[string]string{ - "f": "(x: int) -> int", + "f": "(x: t8290) -> t8290", "y": "42", } scope.LocalRange(func(k string, v values.Value) { diff --git a/semantic/analyze.go b/semantic/analyze.go index a297aaa8ac..b5382e44d1 100644 --- a/semantic/analyze.go +++ b/semantic/analyze.go @@ -341,7 +341,7 @@ func analyzeLiteral(lit ast.Literal) (Literal, error) { case *ast.FloatLiteral: return analyzeFloatLiteral(lit) case *ast.IntegerLiteral: - return analyzeIntegerLiteral(lit) + return analyzePolyNumericLiteral(lit) case *ast.UnsignedIntegerLiteral: return analyzeUnsignedIntegerLiteral(lit) case *ast.RegexpLiteral: @@ -701,10 +701,11 @@ func analyzeFloatLiteral(lit *ast.FloatLiteral) (*FloatLiteral, error) { Value: lit.Value, }, nil } -func analyzeIntegerLiteral(lit *ast.IntegerLiteral) (*IntegerLiteral, error) { - return &IntegerLiteral{ +func analyzePolyNumericLiteral(lit *ast.IntegerLiteral) (*PolyNumericLiteral, error) { + return &PolyNumericLiteral{ Loc: Loc(lit.Location()), Value: lit.Value, + Typ: BasicInt, }, nil } func analyzeUnsignedIntegerLiteral(lit *ast.UnsignedIntegerLiteral) (*UnsignedIntegerLiteral, error) { diff --git a/semantic/ast.go b/semantic/ast.go index d11c688bc7..82a0fe7b53 100644 --- a/semantic/ast.go +++ b/semantic/ast.go @@ -222,7 +222,7 @@ func ToAST(n Node) ast.Node { return &ast.DurationLiteral{Values: n.Values} case *FloatLiteral: return &ast.FloatLiteral{Value: n.Value} - case *IntegerLiteral: + case *PolyNumericLiteral: return &ast.IntegerLiteral{Value: n.Value} case *StringLiteral: return &ast.StringLiteral{Value: n.Value} diff --git a/semantic/ast_test.go b/semantic/ast_test.go index 26d3c30ca9..fb215de581 100644 --- a/semantic/ast_test.go +++ b/semantic/ast_test.go @@ -70,7 +70,7 @@ my_value s: `1.0`, }, { - name: "integer literal", + name: "polynumeric literal", s: `5`, }, { diff --git a/semantic/flatbuffers.go b/semantic/flatbuffers.go index f0268b6cd0..4a80274d42 100644 --- a/semantic/flatbuffers.go +++ b/semantic/flatbuffers.go @@ -256,6 +256,14 @@ func fromExpressionTableOptional(getTable getTableFn, exprType fbsemantic.Expres return nil, err } return e, nil + case fbsemantic.ExpressionPolyNumericLiteral: + fbExpr := new(fbsemantic.PolyNumericLiteral) + fbExpr.Init(tbl.Bytes, tbl.Pos) + e := &PolyNumericLiteral{} + if err := e.FromBuf(fbExpr); err != nil { + return nil, err + } + return e, nil case fbsemantic.ExpressionStringLiteral: fbExpr := new(fbsemantic.StringLiteral) fbExpr.Init(tbl.Bytes, tbl.Pos) diff --git a/semantic/flatbuffers_deserialize.go b/semantic/flatbuffers_deserialize.go index 916c0a8b79..6c2f5e01b2 100644 --- a/semantic/flatbuffers_deserialize.go +++ b/semantic/flatbuffers_deserialize.go @@ -362,6 +362,23 @@ func (rcv *IntegerLiteral) FromBuf(fb *fbsemantic.IntegerLiteral) error { return nil } +func (rcv *PolyNumericLiteral) FromBuf(fb *fbsemantic.PolyNumericLiteral) error { + var err error + if fb == nil { + return nil + } + if fbLoc := fb.Loc(nil); fbLoc != nil { + if err = rcv.Loc.FromBuf(fbLoc); err != nil { + return errors.Wrap(err, codes.Inherit, "PolyNumericLiteral.Loc") + } + } + rcv.Value = fb.Value() + if rcv.Typ, err = getMonoType(fb); err != nil { + return errors.Wrap(err, codes.Inherit, "PolyNumericLiteral.Typ") + } + return nil +} + func (rcv *LogicalExpression) FromBuf(fb *fbsemantic.LogicalExpression) error { var err error if fb == nil { diff --git a/semantic/flatbuffers_test.go b/semantic/flatbuffers_test.go index 2558d834c3..afda7d01cd 100644 --- a/semantic/flatbuffers_test.go +++ b/semantic/flatbuffers_test.go @@ -844,7 +844,7 @@ func TestFlatBuffersRoundTrip(t *testing.T) { name: "native variable assignment", fluxSrc: `x = 42`, types: map[string]string{ - "x": "forall [] int", + "x": "forall [t0] where t0: NumericDefaultInt t0", }, }, { @@ -863,8 +863,8 @@ func TestFlatBuffersRoundTrip(t *testing.T) { x = [1, 2, 3] y = x[2]`, types: map[string]string{ - "x": "forall [] [int]", - "y": "forall [] int", + "x": "forall [t0] where t0: NumericDefaultInt [t0]", + "y": "forall [t0] where t0: NumericDefaultInt t0", }, }, { @@ -890,8 +890,8 @@ func TestFlatBuffersRoundTrip(t *testing.T) { return z }`, types: map[string]string{ - "f": "forall [] (x: int) -> int", - "z": "forall [] int", + "f": "forall [t0] where t0: Addable, t0: NumericDefaultInt (x: t0) -> t0", + "z": "forall [] t0", }, }, { @@ -919,14 +919,14 @@ func TestFlatBuffersRoundTrip(t *testing.T) { name: "default args", fluxSrc: `f = (x=1, y) => x + y`, types: map[string]string{ - "f": "forall [] (?x: int, y: int) -> int", + "f": "forall [t0] where t0: Addable, t0: NumericDefaultInt (?x: t0, y: t0) -> t0", }, }, { name: "two default args", fluxSrc: `f = (x=1, y=10, z) => x + y + z`, types: map[string]string{ - "f": "forall [] (?x: int, ?y: int, z: int) -> int", + "f": "forall [t0] where t0: Addable, t0: NumericDefaultInt (?x: t0, ?y: t0, z: t0) -> t0", }, }, { @@ -949,7 +949,7 @@ func TestFlatBuffersRoundTrip(t *testing.T) { rem = "foo" =~ /foo/ renm = "food" !~ /foog/`, types: map[string]string{ - "x": "forall [] int", + "x": "forall [t0] where t0: Addable, t0: Subtractable, t0: Divisible, t0: NumericDefaultInt t0", "lt": "forall [] bool", "lte": "forall [] bool", "gt": "forall [] bool", @@ -966,8 +966,8 @@ func TestFlatBuffersRoundTrip(t *testing.T) { f = (x) => x + 1 y = f(x: 10)`, types: map[string]string{ - "f": "forall [] (x: int) -> int", - "y": "forall [] int", + "f": "forall [t0] where t0: Addable, t0: NumericDefaultInt (x: t0) -> t0", + "y": "forall [t0] where t0: Addable, t0: NumericDefaultInt t0", }, }, { @@ -977,7 +977,7 @@ func TestFlatBuffersRoundTrip(t *testing.T) { y = f(x: 10, y: 30)`, types: map[string]string{ "f": "forall [t0] where t0: Addable (x: t0, y: t0) -> t0", - "y": "forall [] int", + "y": "forall [t0] where t0: Addable, t0: NumericDefaultInt t0", }, }, { @@ -987,7 +987,7 @@ func TestFlatBuffersRoundTrip(t *testing.T) { y = 30 |> f(x: 10)`, types: map[string]string{ "f": "forall [t0] where t0: Addable (x: t0, <-y: t0) -> t0", - "y": "forall [] int", + "y": "forall [t0] where t0: Addable, t0: NumericDefaultInt t0", }, }, { @@ -1004,8 +1004,8 @@ func TestFlatBuffersRoundTrip(t *testing.T) { x = 34 y = x`, types: map[string]string{ - "x": "forall [] int", - "y": "forall [] int", + "x": "forall [t0] where t0: NumericDefaultInt t0", + "y": "forall [t0] where t0: NumericDefaultInt t0", }, }, { @@ -1041,8 +1041,8 @@ func TestFlatBuffersRoundTrip(t *testing.T) { f = (r) => ({r with val: 32}) o = f(r: {val: "thirty-two"})`, types: map[string]string{ - "f": "forall [t0] (r: t0) -> {val: int | t0}", - "o": "forall [] {val: int | val: string}", + "f": "forall [t0, t1] where t1: NumericDefaultInt (r: t0) -> {val: t1 | t0}", + "o": "forall [t0] where t0: NumericDefaultInt {val: t0 | val: string}", }, }, { @@ -1052,8 +1052,8 @@ func TestFlatBuffersRoundTrip(t *testing.T) { y = +1 b = not false`, types: map[string]string{ - "x": "forall [] int", - "y": "forall [] int", + "x": "forall [t0] where t0: Negatable, t0: NumericDefaultInt t0", + "y": "forall [t0] where t0: Negatable, t0: NumericDefaultInt t0", "b": "forall [] bool", }, }, diff --git a/semantic/format.go b/semantic/format.go index ca8c4caa09..00a425d9c1 100644 --- a/semantic/format.go +++ b/semantic/format.go @@ -70,6 +70,7 @@ func (v *formattingVisitor) Visit(node Node) Visitor { case *StringLiteral: case *FloatLiteral: case *IntegerLiteral: + case *PolyNumericLiteral: case *IdentifierExpression: default: // Do not recurse into unknown nodes, just push an error string @@ -102,6 +103,8 @@ func (v *formattingVisitor) Done(node Node) { v.push(fmt.Sprintf("%f", n.Value)) case *IntegerLiteral: v.push(fmt.Sprintf("%v", n.Value)) + case *PolyNumericLiteral: + v.push(fmt.Sprintf("%v", n.Value)) case *IdentifierExpression: v.push(n.Name) } diff --git a/semantic/graph.go b/semantic/graph.go index 725b4380ee..882e5aa38a 100644 --- a/semantic/graph.go +++ b/semantic/graph.go @@ -61,6 +61,7 @@ func (*BooleanLiteral) node() {} func (*DateTimeLiteral) node() {} func (*DurationLiteral) node() {} func (*FloatLiteral) node() {} +func (*PolyNumericLiteral) node() {} func (*IntegerLiteral) node() {} func (*StringLiteral) node() {} func (*RegexpLiteral) node() {} @@ -104,6 +105,7 @@ func (*DurationLiteral) expression() {} func (*FloatLiteral) expression() {} func (*FunctionExpression) expression() {} func (*IdentifierExpression) expression() {} +func (*PolyNumericLiteral) expression() {} func (*IntegerLiteral) expression() {} func (*LogicalExpression) expression() {} func (*MemberExpression) expression() {} @@ -123,6 +125,7 @@ func (*BooleanLiteral) literal() {} func (*DateTimeLiteral) literal() {} func (*DurationLiteral) literal() {} func (*FloatLiteral) literal() {} +func (*PolyNumericLiteral) literal() {} func (*IntegerLiteral) literal() {} func (*RegexpLiteral) literal() {} func (*StringLiteral) literal() {} @@ -965,6 +968,27 @@ func (e *DurationLiteral) TypeOf() MonoType { return BasicDuration } +type PolyNumericLiteral struct { + Loc + Value int64 + Typ MonoType +} + +func (*PolyNumericLiteral) NodeType() string { return "PolyNumericLiteral" } + +func (l *PolyNumericLiteral) Copy() Node { + if l == nil { + return l + } + nl := new(PolyNumericLiteral) + *nl = *l + + return nl +} +func (e *PolyNumericLiteral) TypeOf() MonoType { + return e.Typ +} + type IntegerLiteral struct { Loc Value int64 diff --git a/semantic/semantictest/cmp.go b/semantic/semantictest/cmp.go index 85025a5095..e561efddff 100644 --- a/semantic/semantictest/cmp.go +++ b/semantic/semantictest/cmp.go @@ -51,6 +51,7 @@ var CmpOptions = []cmp.Option{ cmpopts.IgnoreUnexported(semantic.DateTimeLiteral{}), cmpopts.IgnoreUnexported(semantic.DurationLiteral{}), cmpopts.IgnoreUnexported(semantic.IntegerLiteral{}), + cmpopts.IgnoreUnexported(semantic.PolyNumericLiteral{}), cmpopts.IgnoreUnexported(semantic.FloatLiteral{}), cmpopts.IgnoreUnexported(semantic.RegexpLiteral{}), cmpopts.IgnoreUnexported(semantic.StringLiteral{}), @@ -91,6 +92,7 @@ var CmpOptions = []cmp.Option{ cmpopts.IgnoreFields(semantic.DateTimeLiteral{}, "Loc"), cmpopts.IgnoreFields(semantic.DurationLiteral{}, "Loc"), cmpopts.IgnoreFields(semantic.IntegerLiteral{}, "Loc"), + cmpopts.IgnoreFields(semantic.PolyNumericLiteral{}, "Loc"), cmpopts.IgnoreFields(semantic.FloatLiteral{}, "Loc"), cmpopts.IgnoreFields(semantic.RegexpLiteral{}, "Loc"), cmpopts.IgnoreFields(semantic.StringLiteral{}, "Loc"), @@ -98,6 +100,8 @@ var CmpOptions = []cmp.Option{ cmpopts.IgnoreFields(semantic.StringExpression{}, "Loc"), cmpopts.IgnoreFields(semantic.TextPart{}, "Loc"), cmpopts.IgnoreFields(semantic.InterpolatedPart{}, "Loc"), + + cmpopts.IgnoreFields(semantic.PolyNumericLiteral{}, "Typ"), } func TransformValue(v values.Value) map[string]interface{} { diff --git a/semantic/walk.go b/semantic/walk.go index 117ece3427..fa5c6ca052 100644 --- a/semantic/walk.go +++ b/semantic/walk.go @@ -323,6 +323,11 @@ func walk(v Visitor, n Node) { return } v.Visit(n) + case *PolyNumericLiteral: + if n == nil { + return + } + v.Visit(n) case *RegexpLiteral: if n == nil { return diff --git a/stdlib/experimental/array/from.go b/stdlib/experimental/array/from.go index b19ba8d8a7..345b5c2ca1 100644 --- a/stdlib/experimental/array/from.go +++ b/stdlib/experimental/array/from.go @@ -135,9 +135,17 @@ func buildTable(rows values.Array, mem *memory.Allocator) (flux.Table, error) { if err != nil { return nil, err } + + switch pt.Nature() { + case semantic.Duration, semantic.Regexp, semantic.Array, semantic.Object, semantic.Function: + return nil, errors.Newf(codes.Invalid, "cannot represent the type %v as column data", pt) + } + ctyp := flux.ColumnType(pt) + // With the current implementation of the interpreter, type variables are given the type semantic.Invalid instead of semantic.Int given their polymorphic property. + // Any type variable at this stage of the interpreter must be a polymorphic numeric literal that hasn't already been type inferred and thus we can cast semantic.Invalid types to Ints. if ctyp == flux.TInvalid { - return nil, errors.Newf(codes.Invalid, "cannot represent the type %v as column data", pt) + ctyp = flux.TInt } cols = append(cols, flux.ColMeta{ Label: rp.Name(), diff --git a/values/binary.go b/values/binary.go index 1eaf35ab68..df2ff73b61 100644 --- a/values/binary.go +++ b/values/binary.go @@ -69,6 +69,16 @@ var binaryFuncLookup = map[BinaryFuncSignature]BinaryFunction{ r := rv.Float() return NewFloat(l + r), nil }, + {Operator: ast.AdditionOperator, Left: semantic.Int, Right: semantic.Float}: func(lv, rv Value) (Value, error) { + l := lv.Int() + r := rv.Float() + return NewFloat(float64(l) + r), nil + }, + {Operator: ast.AdditionOperator, Left: semantic.Float, Right: semantic.Int}: func(lv, rv Value) (Value, error) { + l := lv.Float() + r := rv.Int() + return NewFloat(l + float64(r)), nil + }, {Operator: ast.AdditionOperator, Left: semantic.String, Right: semantic.String}: func(lv, rv Value) (Value, error) { l := lv.Str() r := rv.Str() @@ -95,6 +105,16 @@ var binaryFuncLookup = map[BinaryFuncSignature]BinaryFunction{ r := rv.Float() return NewFloat(l - r), nil }, + {Operator: ast.SubtractionOperator, Left: semantic.Int, Right: semantic.Float}: func(lv, rv Value) (Value, error) { + l := lv.Int() + r := rv.Float() + return NewFloat(float64(l) - r), nil + }, + {Operator: ast.SubtractionOperator, Left: semantic.Float, Right: semantic.Int}: func(lv, rv Value) (Value, error) { + l := lv.Float() + r := rv.Int() + return NewFloat(l - float64(r)), nil + }, {Operator: ast.SubtractionOperator, Left: semantic.Duration, Right: semantic.Duration}: func(lv, rv Value) (Value, error) { l := lv.Duration() r := rv.Duration() @@ -116,6 +136,16 @@ var binaryFuncLookup = map[BinaryFuncSignature]BinaryFunction{ r := rv.Float() return NewFloat(l * r), nil }, + {Operator: ast.MultiplicationOperator, Left: semantic.Int, Right: semantic.Float}: func(lv, rv Value) (Value, error) { + l := lv.Int() + r := rv.Float() + return NewFloat(float64(l) * r), nil + }, + {Operator: ast.MultiplicationOperator, Left: semantic.Float, Right: semantic.Int}: func(lv, rv Value) (Value, error) { + l := lv.Float() + r := rv.Int() + return NewFloat(l * float64(r)), nil + }, {Operator: ast.DivisionOperator, Left: semantic.Int, Right: semantic.Int}: func(lv, rv Value) (Value, error) { l := lv.Int() r := rv.Int() @@ -140,6 +170,22 @@ var binaryFuncLookup = map[BinaryFuncSignature]BinaryFunction{ } return NewFloat(l / r), nil }, + {Operator: ast.DivisionOperator, Left: semantic.Int, Right: semantic.Float}: func(lv, rv Value) (Value, error) { + l := lv.Int() + r := rv.Float() + if r == 0 { + return nil, errors.Newf(codes.FailedPrecondition, "cannot divide by zero") + } + return NewFloat(float64(l) / r), nil + }, + {Operator: ast.DivisionOperator, Left: semantic.Float, Right: semantic.Int}: func(lv, rv Value) (Value, error) { + l := lv.Float() + r := rv.Int() + if r == 0 { + return nil, errors.Newf(codes.FailedPrecondition, "cannot divide by zero") + } + return NewFloat(l / float64(r)), nil + }, {Operator: ast.ModuloOperator, Left: semantic.Int, Right: semantic.Int}: func(lv, rv Value) (Value, error) { l := lv.Int() r := rv.Int() @@ -164,6 +210,22 @@ var binaryFuncLookup = map[BinaryFuncSignature]BinaryFunction{ } return NewFloat(math.Mod(l, r)), nil }, + {Operator: ast.ModuloOperator, Left: semantic.Int, Right: semantic.Float}: func(lv, rv Value) (Value, error) { + l := lv.Int() + r := rv.Float() + if r == 0 { + return nil, errors.Newf(codes.FailedPrecondition, "cannot divide by zero") + } + return NewFloat(math.Mod(float64(l), r)), nil + }, + {Operator: ast.ModuloOperator, Left: semantic.Float, Right: semantic.Int}: func(lv, rv Value) (Value, error) { + l := lv.Float() + r := rv.Int() + if r == 0 { + return nil, errors.Newf(codes.FailedPrecondition, "cannot divide by zero") + } + return NewFloat(math.Mod(l, float64(r))), nil + }, {Operator: ast.PowerOperator, Left: semantic.Int, Right: semantic.Int}: func(lv, rv Value) (Value, error) { l := lv.Int() r := rv.Int() @@ -179,6 +241,17 @@ var binaryFuncLookup = map[BinaryFuncSignature]BinaryFunction{ r := rv.Float() return NewFloat(math.Pow(float64(l), float64(r))), nil }, + {Operator: ast.PowerOperator, Left: semantic.Int, Right: semantic.Float}: func(lv, rv Value) (Value, error) { + l := lv.Int() + r := rv.Float() + return NewFloat(math.Pow(float64(l), float64(r))), nil + }, + {Operator: ast.PowerOperator, Left: semantic.Float, Right: semantic.Int}: func(lv, rv Value) (Value, error) { + l := lv.Float() + r := rv.Int() + return NewFloat(math.Pow(float64(l), float64(r))), nil + }, + //--------------------- // Comparison Operators //---------------------