diff --git a/executable_semantics/BUILD b/executable_semantics/BUILD index 8740148d1a73..df7d39f254bc 100644 --- a/executable_semantics/BUILD +++ b/executable_semantics/BUILD @@ -74,6 +74,8 @@ EXAMPLES = [ "tuple1", "tuple2", "tuple3", + "tuple4", + "tuple5", "while1", "zero", "experimental_continuation1", diff --git a/executable_semantics/ast/expression.cpp b/executable_semantics/ast/expression.cpp index 6303a107e35b..3b12394b2e27 100644 --- a/executable_semantics/ast/expression.cpp +++ b/executable_semantics/ast/expression.cpp @@ -150,10 +150,19 @@ auto MakeTuple(int line_num, e->line_num = line_num; e->tag = ExpressionKind::Tuple; int i = 0; + bool seen_named_member = false; for (auto& arg : *args) { if (arg.first == "") { + if (seen_named_member) { + std::cerr << line_num + << ": positional members must come before named members" + << std::endl; + exit(-1); + } arg.first = std::to_string(i); ++i; + } else { + seen_named_member = true; } } e->u.tuple.fields = args; diff --git a/executable_semantics/interpreter/typecheck.cpp b/executable_semantics/interpreter/typecheck.cpp index 763d1c76a336..799de86bd3b0 100644 --- a/executable_semantics/interpreter/typecheck.cpp +++ b/executable_semantics/interpreter/typecheck.cpp @@ -13,6 +13,7 @@ #include "executable_semantics/ast/function_definition.h" #include "executable_semantics/interpreter/interpreter.h" +#include "executable_semantics/tracing_flag.h" namespace Carbon { @@ -96,6 +97,26 @@ auto ReifyType(const Value* t, int line_num) -> const Expression* { // whether it's a position that expects a value, a pattern, or a type. auto TypeCheckExp(const Expression* e, TypeEnv types, Env values, const Value* expected, TCContext context) -> TCResult { + if (tracing_output) { + switch (context) { + case TCContext::ValueContext: + std::cout << "checking expression "; + break; + case TCContext::PatternContext: + std::cout << "checking pattern, "; + if (expected) { + std::cout << "expecting "; + PrintValue(expected, std::cerr); + } + std::cout << ", "; + break; + case TCContext::TypeContext: + std::cout << "checking type "; + break; + } + PrintExp(e); + std::cout << std::endl; + } switch (e->tag) { case ExpressionKind::PatternVariable: { if (context != TCContext::PatternContext) { @@ -116,6 +137,8 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values, } else { t = expected; } + } else if (expected) { + ExpectType(e->line_num, "pattern variable", t, expected); } auto new_e = MakeVarPat(e->line_num, *e->u.pattern_variable.name, ReifyType(t, e->line_num)); @@ -153,19 +176,32 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values, new std::vector>(); auto arg_types = new std::vector>(); auto new_types = types; + if (expected && expected->tag != ValKind::TupleV) { + std::cerr << e->line_num << ": compilation error, didn't expect a tuple" + << std::endl; + exit(-1); + } + if (expected && + e->u.tuple.fields->size() != expected->u.tuple.elts->size()) { + std::cerr << e->line_num + << ": compilation error, tuples of different length" + << std::endl; + exit(-1); + } int i = 0; for (auto arg = e->u.tuple.fields->begin(); arg != e->u.tuple.fields->end(); ++arg, ++i) { const Value* arg_expected = nullptr; if (expected && expected->tag == ValKind::TupleV) { - std::optional
expected_field = - FindTupleField(arg->first, expected); - if (expected_field == std::nullopt) { - std::cerr << e->line_num << ": compilation error, missing field " - << arg->first << std::endl; + if ((*expected->u.tuple.elts)[i].first != arg->first) { + std::cerr << e->line_num + << ": compilation error, field names do not match, " + << "expected " << (*expected->u.tuple.elts)[i].first + << " but got " << arg->first << std::endl; exit(-1); } - arg_expected = state->ReadFromMemory(*expected_field, e->line_num); + arg_expected = state->ReadFromMemory( + (*expected->u.tuple.elts)[i].second, e->line_num); } auto arg_res = TypeCheckExp(arg->second, new_types, values, arg_expected, context); diff --git a/executable_semantics/interpreter/value.cpp b/executable_semantics/interpreter/value.cpp index 0d135e0ec7fb..22443d28336e 100644 --- a/executable_semantics/interpreter/value.cpp +++ b/executable_semantics/interpreter/value.cpp @@ -339,13 +339,12 @@ auto TypeEqual(const Value* t1, const Value* t2) -> bool { return false; } for (size_t i = 0; i < t1->u.tuple.elts->size(); ++i) { - std::optional
t2_field = - FindTupleField((*t1->u.tuple.elts)[i].first, t2); - if (t2_field == std::nullopt) { + if ((*t1->u.tuple.elts)[i].first != (*t2->u.tuple.elts)[i].first) { return false; } - if (!TypeEqual(state->ReadFromMemory((*t1->u.tuple.elts)[i].second, 0), - state->ReadFromMemory(*t2_field, 0))) { + if (!TypeEqual( + state->ReadFromMemory((*t1->u.tuple.elts)[i].second, 0), + state->ReadFromMemory((*t2->u.tuple.elts)[i].second, 0))) { return false; } } @@ -354,9 +353,13 @@ auto TypeEqual(const Value* t1, const Value* t2) -> bool { case ValKind::IntTV: case ValKind::BoolTV: case ValKind::ContinuationTV: + case ValKind::TypeTV: return true; default: std::cerr << "TypeEqual used to compare non-type values" << std::endl; + PrintValue(t1, std::cerr); + std::cerr << std::endl; + PrintValue(t2, std::cerr); exit(-1); } } diff --git a/executable_semantics/testdata/fun6_fail_type.golden b/executable_semantics/testdata/fun6_fail_type.golden index 2bf75beeaf9f..ee66c8fc753d 100644 --- a/executable_semantics/testdata/fun6_fail_type.golden +++ b/executable_semantics/testdata/fun6_fail_type.golden @@ -1,4 +1,2 @@ -10: type error in call -expected: (0 = Int@0, 1 = Int@1) -actual: (0 = (0 = Int@6, 1 = Int@7)@8) +10: compilation error, tuples of different length EXIT CODE: 255 diff --git a/executable_semantics/testdata/fun_named_params2.6c b/executable_semantics/testdata/fun_named_params2.6c index ae8e7adf0a30..7aba92ccf5c1 100644 --- a/executable_semantics/testdata/fun_named_params2.6c +++ b/executable_semantics/testdata/fun_named_params2.6c @@ -5,5 +5,5 @@ fn f(Int: x, .d = Int: y, Int: z, .e = Int: a) => (x + y) - (z + a); fn main() -> Int { - return f(.e = 2, 1, 3, .d = 4); + return 0; } diff --git a/executable_semantics/testdata/fun_named_params2.golden b/executable_semantics/testdata/fun_named_params2.golden index 2db2755da426..bc8b77c483eb 100644 --- a/executable_semantics/testdata/fun_named_params2.golden +++ b/executable_semantics/testdata/fun_named_params2.golden @@ -1 +1,2 @@ -result: 0 +5: positional members must come before named members +EXIT CODE: 255 diff --git a/executable_semantics/testdata/match_type.6c b/executable_semantics/testdata/match_type.6c index a7714b8c0bfd..324d81d3013d 100644 --- a/executable_semantics/testdata/match_type.6c +++ b/executable_semantics/testdata/match_type.6c @@ -5,12 +5,8 @@ fn main() -> Int { var auto: t = fnty (Int,Int); var Int: x = 0; -// match (t) { -// case fnty(Int: x, Int: y): z => -// x = x - 1; -// } match (t) { - case fnty(Int,Int): z => + case Type: z => x = x + 1; } match (t) { diff --git a/executable_semantics/testdata/record1.6c b/executable_semantics/testdata/record1.6c index 661685e4a99e..9894837a9faa 100644 --- a/executable_semantics/testdata/record1.6c +++ b/executable_semantics/testdata/record1.6c @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception fn main() -> Int { - var (.x = Int, .y = Int): t2 = (.y = 5, .x = 2); + var (.x = Int, .y = Int): t2 = (.x = 2, .y = 5); t2.y = 3; return t2.y - t2.x - 1; // 3 - 2 - 1 } diff --git a/executable_semantics/testdata/tuple3.6c b/executable_semantics/testdata/tuple3.6c index 015ccd5d9781..adae5da59ff5 100644 --- a/executable_semantics/testdata/tuple3.6c +++ b/executable_semantics/testdata/tuple3.6c @@ -3,6 +3,6 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception fn main() -> Int { - var (.x = Int, Int): t = (3, .x = 2); + var (Int, .x = Int): t = (3, .x = 2); return t.x + 1 - t[0]; } diff --git a/executable_semantics/testdata/tuple4.6c b/executable_semantics/testdata/tuple4.6c new file mode 100644 index 000000000000..2d4bd3d38830 --- /dev/null +++ b/executable_semantics/testdata/tuple4.6c @@ -0,0 +1,8 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +fn main() -> Int { + var auto: t = (.x = 2, 3); + return 0; +} diff --git a/executable_semantics/testdata/tuple4.golden b/executable_semantics/testdata/tuple4.golden new file mode 100644 index 000000000000..6bb96d92a524 --- /dev/null +++ b/executable_semantics/testdata/tuple4.golden @@ -0,0 +1,2 @@ +6: positional members must come before named members +EXIT CODE: 255 diff --git a/executable_semantics/testdata/tuple5.6c b/executable_semantics/testdata/tuple5.6c new file mode 100644 index 000000000000..c36e695db741 --- /dev/null +++ b/executable_semantics/testdata/tuple5.6c @@ -0,0 +1,10 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// Test the that field order matters for tuples. + +fn main() -> Int { + var (.x = Int, .y = Int): t = (.y = 2, .x = 3); + return 0; +} diff --git a/executable_semantics/testdata/tuple5.golden b/executable_semantics/testdata/tuple5.golden new file mode 100644 index 000000000000..bd957fe75194 --- /dev/null +++ b/executable_semantics/testdata/tuple5.golden @@ -0,0 +1,4 @@ +8: type error in pattern variable +expected: (x = Int@2, y = Int@3) +actual: (y = Int@0, x = Int@1) +EXIT CODE: 255 diff --git a/executable_semantics/testdata/tuple_equality3.6c b/executable_semantics/testdata/tuple_equality3.6c index b958b18d81c6..185f02e0307e 100644 --- a/executable_semantics/testdata/tuple_equality3.6c +++ b/executable_semantics/testdata/tuple_equality3.6c @@ -4,7 +4,7 @@ fn main() -> Int { var (Int,Int): t1 = (5, 2); - var (Int,Int): t2 = (5,); + var (Int,): t2 = (5,); if (t1 == t2) { return 1; } else { diff --git a/executable_semantics/testdata/tuple_equality3.golden b/executable_semantics/testdata/tuple_equality3.golden index 2db2755da426..1917c6bbe8d6 100644 --- a/executable_semantics/testdata/tuple_equality3.golden +++ b/executable_semantics/testdata/tuple_equality3.golden @@ -1 +1,4 @@ -result: 0 +8: type error in == +expected: (0 = Int@2, 1 = Int@3) +actual: (0 = Int@5) +EXIT CODE: 255 diff --git a/executable_semantics/testdata/tuple_match2.6c b/executable_semantics/testdata/tuple_match2.6c index dc2620cfb7e4..3af74a674632 100644 --- a/executable_semantics/testdata/tuple_match2.6c +++ b/executable_semantics/testdata/tuple_match2.6c @@ -5,7 +5,7 @@ // Test matching with a mixture of positional and named fields. fn main() -> Int { - var auto: t = (.x = 5, 2); + var auto: t = (2, .x = 5); match (t) { case (auto: a, .x = auto: b) => return a - b + 3;