diff --git a/executable_semantics/BUILD b/executable_semantics/BUILD index 580da61a327c..e980a335f67c 100644 --- a/executable_semantics/BUILD +++ b/executable_semantics/BUILD @@ -36,6 +36,14 @@ EXAMPLES = [ "fun5", "fun6_fail_type", "funptr1", + "generic_function1", + "generic_function2", + "generic_function3", + "generic_function_apply", + "generic_function_fail1", + "generic_function_fail2", + "generic_function_swap", + "generic_function_tuple_map", "global_variable1", "global_variable2", "global_variable3", diff --git a/executable_semantics/ast/function_definition.cpp b/executable_semantics/ast/function_definition.cpp index 15eb91dc95db..615e4510e86e 100644 --- a/executable_semantics/ast/function_definition.cpp +++ b/executable_semantics/ast/function_definition.cpp @@ -7,7 +7,21 @@ namespace Carbon { void FunctionDefinition::PrintDepth(int depth, llvm::raw_ostream& out) const { - out << "fn " << name << " " << *param_pattern << " -> " << *return_type; + out << "fn " << name << " "; + if (deduced_parameters.size() > 0) { + out << "["; + unsigned int i = 0; + for (const auto& deduced : deduced_parameters) { + if (i != 0) { + out << ", "; + } + out << deduced.name << ":! "; + deduced.type->Print(out); + ++i; + } + out << "]"; + } + out << *param_pattern << " -> " << *return_type; if (body) { out << " {\n"; body->PrintDepth(depth, out); diff --git a/executable_semantics/ast/function_definition.h b/executable_semantics/ast/function_definition.h index 1bc833c4f839..ca64123558c8 100644 --- a/executable_semantics/ast/function_definition.h +++ b/executable_semantics/ast/function_definition.h @@ -11,13 +11,22 @@ namespace Carbon { +// TODO: expand the kinds of things that can be deduced parameters. +// For now, only generic parameters are supported. +struct GenericBinding { + std::string name; + const Expression* type; +}; + struct FunctionDefinition { FunctionDefinition() = default; FunctionDefinition(int line_num, std::string name, + std::vector deduced_params, const Expression* param_pattern, const Expression* return_type, const Statement* body) : line_num(line_num), name(std::move(name)), + deduced_parameters(deduced_params), param_pattern(param_pattern), return_type(return_type), body(body) {} @@ -27,6 +36,7 @@ struct FunctionDefinition { int line_num; std::string name; + std::vector deduced_parameters; const Expression* param_pattern; const Expression* return_type; const Statement* body; diff --git a/executable_semantics/interpreter/BUILD b/executable_semantics/interpreter/BUILD index de9f37d0366d..49118772d1bb 100644 --- a/executable_semantics/interpreter/BUILD +++ b/executable_semantics/interpreter/BUILD @@ -118,6 +118,7 @@ cc_library( ":containers", ":field_path", "//common:ostream", + "//executable_semantics/ast:function_definition", "//executable_semantics/ast:statement", ], ) diff --git a/executable_semantics/interpreter/interpreter.cpp b/executable_semantics/interpreter/interpreter.cpp index cd7aba041381..1f933a0d5b1d 100644 --- a/executable_semantics/interpreter/interpreter.cpp +++ b/executable_semantics/interpreter/interpreter.cpp @@ -109,7 +109,14 @@ void InitEnv(const Declaration& d, Env* env) { case DeclarationKind::FunctionDeclaration: { const FunctionDefinition& func_def = d.GetFunctionDeclaration().definition; - auto pt = InterpExp(*env, func_def.param_pattern); + Env new_env = *env; + // Bring the deduced parameters into scope. + for (const auto& deduced : func_def.deduced_parameters) { + Address a = + state->heap.AllocateValue(Value::MakeVariableType(deduced.name)); + new_env.Set(deduced.name, a); + } + auto pt = InterpExp(new_env, func_def.param_pattern); auto f = Value::MakeFunctionValue(func_def.name, pt, func_def.body); Address a = state->heap.AllocateValue(f); env->Set(func_def.name, a); @@ -740,7 +747,7 @@ void StepExp() { // { { rt :: fn pt -> [] :: C, E, F} :: S, H} // -> { fn pt -> rt :: {C, E, F} :: S, H} const Value* v = - Value::MakeFunctionType(act->results[0], act->results[1]); + Value::MakeFunctionType({}, act->results[0], act->results[1]); frame->todo.Pop(1); frame->todo.Push(Action::MakeValAction(v)); } diff --git a/executable_semantics/interpreter/typecheck.cpp b/executable_semantics/interpreter/typecheck.cpp index a65e2cf5864a..753c155b6eae 100644 --- a/executable_semantics/interpreter/typecheck.cpp +++ b/executable_semantics/interpreter/typecheck.cpp @@ -65,12 +65,158 @@ auto ReifyType(const Value* t, int line_num) -> const Expression* { case ValKind::PointerType: return Expression::MakePrimitiveOperatorExpression( 0, Operator::Ptr, {ReifyType(t->GetPointerType().type, line_num)}); + case ValKind::VariableType: + return Expression::MakeIdentifierExpression(0, t->GetVariableType().name); default: llvm::errs() << line_num << ": expected a type, not " << *t << "\n"; exit(-1); } } +// Perform type argument deduction, matching the parameter type `param` +// against the argument type `arg`. Whenever there is an VariableType +// in the parameter type, it is deduced to be the corresponding type +// inside the argument type. +// The `deduced` parameter is an accumulator, that is, it holds the +// results so-far. +auto ArgumentDeduction(int line_num, TypeEnv deduced, const Value* param, + const Value* arg) -> TypeEnv { + switch (param->tag()) { + case ValKind::VariableType: { + std::optional d = + deduced.Get(param->GetVariableType().name); + if (!d) { + deduced.Set(param->GetVariableType().name, arg); + } else { + ExpectType(line_num, "argument deduction", *d, arg); + } + return deduced; + } + case ValKind::TupleValue: { + if (arg->tag() != ValKind::TupleValue) { + ExpectType(line_num, "argument deduction", param, arg); + } + if (param->GetTupleValue().elements.size() != + arg->GetTupleValue().elements.size()) { + ExpectType(line_num, "argument deduction", param, arg); + } + for (size_t i = 0; i < param->GetTupleValue().elements.size(); ++i) { + if (param->GetTupleValue().elements[i].name != + arg->GetTupleValue().elements[i].name) { + std::cerr << line_num << ": mismatch in tuple names, " + << param->GetTupleValue().elements[i].name + << " != " << arg->GetTupleValue().elements[i].name + << std::endl; + exit(-1); + } + deduced = ArgumentDeduction(line_num, deduced, + param->GetTupleValue().elements[i].value, + arg->GetTupleValue().elements[i].value); + } + return deduced; + } + case ValKind::FunctionType: { + if (arg->tag() != ValKind::FunctionType) { + ExpectType(line_num, "argument deduction", param, arg); + } + // TODO: handle situation when arg has deduced parameters. + deduced = + ArgumentDeduction(line_num, deduced, param->GetFunctionType().param, + arg->GetFunctionType().param); + deduced = + ArgumentDeduction(line_num, deduced, param->GetFunctionType().ret, + arg->GetFunctionType().ret); + return deduced; + } + case ValKind::PointerType: { + if (arg->tag() != ValKind::PointerType) { + ExpectType(line_num, "argument deduction", param, arg); + } + return ArgumentDeduction(line_num, deduced, param->GetPointerType().type, + arg->GetPointerType().type); + } + // Nothing to do in the case for `auto`. + case ValKind::AutoType: { + return deduced; + } + // For the following cases, we check for type equality. + case ValKind::ContinuationType: + case ValKind::StructType: + case ValKind::ChoiceType: + case ValKind::IntType: + case ValKind::BoolType: + case ValKind::TypeType: { + ExpectType(line_num, "argument deduction", param, arg); + return deduced; + } + // The rest of these cases should never happen. + case ValKind::IntValue: + case ValKind::BoolValue: + case ValKind::FunctionValue: + case ValKind::PointerValue: + case ValKind::StructValue: + case ValKind::AlternativeValue: + case ValKind::BindingPlaceholderValue: + case ValKind::AlternativeConstructorValue: + case ValKind::ContinuationValue: + llvm::errs() << line_num + << ": internal error in ArgumentDeduction: expected type, " + << "not value " << *param << "\n"; + exit(-1); + } +} + +auto Substitute(TypeEnv dict, const Value* type) -> const Value* { + switch (type->tag()) { + case ValKind::VariableType: { + std::optional t = dict.Get(type->GetVariableType().name); + if (!t) { + return type; + } else { + return *t; + } + } + case ValKind::TupleValue: { + std::vector elts; + for (const auto& elt : type->GetTupleValue().elements) { + auto t = Substitute(dict, elt.value); + elts.push_back({.name = elt.name, .value = t}); + } + return Value::MakeTupleValue(elts); + } + case ValKind::FunctionType: { + auto param = Substitute(dict, type->GetFunctionType().param); + auto ret = Substitute(dict, type->GetFunctionType().ret); + return Value::MakeFunctionType({}, param, ret); + } + case ValKind::PointerType: { + return Value::MakePointerType( + Substitute(dict, type->GetPointerType().type)); + } + case ValKind::AutoType: + case ValKind::IntType: + case ValKind::BoolType: + case ValKind::TypeType: + case ValKind::StructType: + case ValKind::ChoiceType: + case ValKind::ContinuationType: + return type; + // The rest of these cases should never happen. + case ValKind::IntValue: + case ValKind::BoolValue: + case ValKind::FunctionValue: + case ValKind::PointerValue: + case ValKind::StructValue: + case ValKind::AlternativeValue: + case ValKind::BindingPlaceholderValue: + case ValKind::AlternativeConstructorValue: + case ValKind::ContinuationValue: + llvm::errs() << "internal error in Substitute: expected type, " + << "not value " << *type << "\n"; + exit(-1); + } +} + // The TypeCheckExp function performs semantic analysis on an expression. // It returns a new version of the expression, its type, and an // updated environment which are bundled into a TCResult object. @@ -251,7 +397,7 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values, if (e->GetFieldAccessExpression().field == vt->first) { const Expression* new_e = Expression::MakeFieldAccessExpression( e->line_num, res.exp, e->GetFieldAccessExpression().field); - auto fun_ty = Value::MakeFunctionType(vt->second, t); + auto fun_ty = Value::MakeFunctionType({}, vt->second, t); return TCResult(new_e, fun_ty, res.types); } } @@ -350,11 +496,30 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values, auto arg_res = TypeCheckExp(e->GetCallExpression().argument, fun_res.types, values, fun_t->GetFunctionType().param, context); - ExpectType(e->line_num, "call", fun_t->GetFunctionType().param, - arg_res.type); + auto parameter_type = fun_t->GetFunctionType().param; + auto return_type = fun_t->GetFunctionType().ret; + if (fun_t->GetFunctionType().deduced.size() > 0) { + auto deduced_args = ArgumentDeduction(e->line_num, TypeEnv(), + parameter_type, arg_res.type); + for (auto& deduced_param : fun_t->GetFunctionType().deduced) { + // TODO: change the following to a CHECK once the real checking + // has been added to the type checking of function signatures. + if (!deduced_args.Get(deduced_param.name)) { + std::cerr << e->line_num + << ": error, could not deduce type argument for type " + "parameter " + << deduced_param.name << std::endl; + exit(-1); + } + } + parameter_type = Substitute(deduced_args, parameter_type); + return_type = Substitute(deduced_args, return_type); + } else { + ExpectType(e->line_num, "call", parameter_type, arg_res.type); + } auto new_e = Expression::MakeCallExpression(e->line_num, fun_res.exp, arg_res.exp); - return TCResult(new_e, fun_t->GetFunctionType().ret, arg_res.types); + return TCResult(new_e, return_type, arg_res.types); } default: { llvm::errs() << e->line_num @@ -622,10 +787,23 @@ auto CheckOrEnsureReturn(const Statement* stmt, bool void_return, int line_num) } } +// TODO: factor common parts of TypeCheckFunDef and TypeOfFunDef into +// a function. +// TODO: Add checking to function definitions to ensure that +// all deduced type parameters will be deduced. auto TypeCheckFunDef(const FunctionDefinition* f, TypeEnv types, Env values) -> struct FunctionDefinition* { + // Bring the deduced parameters into scope + for (const auto& deduced : f->deduced_parameters) { + // auto t = InterpExp(values, deduced.type); + Address a = + state->heap.AllocateValue(Value::MakeVariableType(deduced.name)); + values.Set(deduced.name, a); + } + // Type check the parameter pattern auto param_res = TypeCheckExp(f->param_pattern, types, values, nullptr, TCContext::PatternContext); + // Evaluate the return type expression auto return_type = InterpExp(values, f->return_type); if (f->name == "main") { ExpectType(f->line_num, "return type of `main`", Value::MakeIntType(), @@ -635,20 +813,31 @@ auto TypeCheckFunDef(const FunctionDefinition* f, TypeEnv types, Env values) auto res = TypeCheckStmt(f->body, param_res.types, values, return_type); bool void_return = TypeEqual(return_type, Value::MakeUnitTypeVal()); auto body = CheckOrEnsureReturn(res.stmt, void_return, f->line_num); - return new FunctionDefinition(f->line_num, f->name, f->param_pattern, + return new FunctionDefinition(f->line_num, f->name, f->deduced_parameters, + f->param_pattern, ReifyType(return_type, f->line_num), body); } auto TypeOfFunDef(TypeEnv types, Env values, const FunctionDefinition* fun_def) -> const Value* { + // Bring the deduced parameters into scope + for (const auto& deduced : fun_def->deduced_parameters) { + // auto t = InterpExp(values, deduced.type); + Address a = + state->heap.AllocateValue(Value::MakeVariableType(deduced.name)); + values.Set(deduced.name, a); + } + // Type check the parameter pattern auto param_res = TypeCheckExp(fun_def->param_pattern, types, values, nullptr, TCContext::PatternContext); + // Evaluate the return type expression auto ret = InterpExp(values, fun_def->return_type); if (ret->tag() == ValKind::AutoType) { auto f = TypeCheckFunDef(fun_def, types, values); ret = InterpExp(values, f->return_type); } - return Value::MakeFunctionType(param_res.type, ret); + return Value::MakeFunctionType(fun_def->deduced_parameters, param_res.type, + ret); } auto TypeOfStructDef(const StructDefinition* sd, TypeEnv /*types*/, Env ct_top) @@ -741,7 +930,7 @@ static void TopLevel(const Declaration& d, TypeCheckContext* tops) { field_types.push_back({.name = field_name, .value = field_value}); } auto fun_ty = Value::MakeFunctionType( - Value::MakeTupleValue(std::move(field_types)), st); + {}, Value::MakeTupleValue(std::move(field_types)), st); tops->types.Set(struct_def.name, fun_ty); break; } diff --git a/executable_semantics/interpreter/value.cpp b/executable_semantics/interpreter/value.cpp index 6f9491bfc21e..499229e25f31 100644 --- a/executable_semantics/interpreter/value.cpp +++ b/executable_semantics/interpreter/value.cpp @@ -64,6 +64,10 @@ auto Value::GetChoiceType() const -> const ChoiceType& { return std::get(value); } +auto Value::GetVariableType() const -> const VariableType& { + return std::get(value); +} + auto Value::GetContinuationValue() const -> const ContinuationValue& { return std::get(value); } @@ -207,10 +211,12 @@ auto Value::MakeAutoType() -> const Value* { return v; } -auto Value::MakeFunctionType(const Value* param, const Value* ret) +auto Value::MakeFunctionType(std::vector deduced_params, + const Value* param, const Value* ret) -> const Value* { auto* v = new Value(); - v->value = FunctionType({.param = param, .ret = ret}); + v->value = FunctionType( + {.deduced = std::move(deduced_params), .param = param, .ret = ret}); return v; } @@ -242,6 +248,12 @@ auto Value::MakeChoiceType(std::string name, VarValues alts) -> const Value* { return v; } +auto Value::MakeVariableType(std::string name) -> const Value* { + auto* v = new Value(); + v->value = VariableType({.name = std::move(name)}); + return v; +} + namespace { auto GetMember(const Value* v, const std::string& f, int line_num) @@ -405,8 +417,20 @@ void Value::Print(llvm::raw_ostream& out) const { out << *GetPointerType().type << "*"; break; case ValKind::FunctionType: - out << "fn " << *GetFunctionType().param << " -> " - << *GetFunctionType().ret; + out << "fn "; + if (GetFunctionType().deduced.size() > 0) { + out << "["; + unsigned int i = 0; + for (const auto& deduced : GetFunctionType().deduced) { + if (i != 0) { + out << ", "; + } + out << deduced.name << ":! " << *deduced.type; + ++i; + } + out << "]"; + } + out << *GetFunctionType().param << " -> " << *GetFunctionType().ret; break; case ValKind::StructType: out << "struct " << GetStructType().name; @@ -414,6 +438,9 @@ void Value::Print(llvm::raw_ostream& out) const { case ValKind::ChoiceType: out << "choice " << GetChoiceType().name; break; + case ValKind::VariableType: + out << GetVariableType().name; + break; case ValKind::ContinuationValue: out << "continuation"; // TODO: Find a way to print useful information about the continuation @@ -457,6 +484,7 @@ auto CopyVal(const Value* val, int line_num) -> const Value* { return val; case ValKind::FunctionType: return Value::MakeFunctionType( + val->GetFunctionType().deduced, CopyVal(val->GetFunctionType().param, line_num), CopyVal(val->GetFunctionType().ret, line_num)); @@ -473,6 +501,7 @@ auto CopyVal(const Value* val, int line_num) -> const Value* { return Value::MakeAutoType(); case ValKind::ContinuationType: return Value::MakeContinuationType(); + case ValKind::VariableType: case ValKind::StructType: case ValKind::ChoiceType: case ValKind::BindingPlaceholderValue: @@ -519,6 +548,8 @@ auto TypeEqual(const Value* t1, const Value* t2) -> bool { case ValKind::ContinuationType: case ValKind::TypeType: return true; + case ValKind::VariableType: + return t1->GetVariableType().name == t2->GetVariableType().name; default: llvm::errs() << "TypeEqual used to compare non-type values\n" << *t1 << "\n" diff --git a/executable_semantics/interpreter/value.h b/executable_semantics/interpreter/value.h index 4417096c4c88..6d99d1a398f6 100644 --- a/executable_semantics/interpreter/value.h +++ b/executable_semantics/interpreter/value.h @@ -12,6 +12,7 @@ #include #include "common/ostream.h" +#include "executable_semantics/ast/function_definition.h" #include "executable_semantics/ast/statement.h" #include "executable_semantics/interpreter/address.h" #include "executable_semantics/interpreter/field_path.h" @@ -52,6 +53,7 @@ enum class ValKind { StructType, ChoiceType, ContinuationType, // The type of a continuation. + VariableType, // e.g. generic type parameters BindingPlaceholderValue, AlternativeConstructorValue, ContinuationValue // A first-class continuation value. @@ -130,6 +132,7 @@ struct TypeType { struct FunctionType { static constexpr ValKind Kind = ValKind::FunctionType; + std::vector deduced; const Value* param; const Value* ret; }; @@ -160,6 +163,11 @@ struct ContinuationType { static constexpr ValKind Kind = ValKind::ContinuationType; }; +struct VariableType { + static constexpr ValKind Kind = ValKind::VariableType; + std::string name; +}; + struct ContinuationValue { static constexpr ValKind Kind = ValKind::ContinuationValue; std::vector stack; @@ -192,13 +200,15 @@ struct Value { static auto MakeAutoType() -> const Value*; static auto MakeBoolType() -> const Value*; static auto MakeTypeType() -> const Value*; - static auto MakeFunctionType(const Value* param, const Value* ret) + static auto MakeFunctionType(std::vector deduced_params, + const Value* param, const Value* ret) -> const Value*; static auto MakePointerType(const Value* type) -> const Value*; static auto MakeStructType(std::string name, VarValues fields, VarValues methods) -> const Value*; static auto MakeUnitTypeVal() -> const Value*; static auto MakeChoiceType(std::string name, VarValues alts) -> const Value*; + static auto MakeVariableType(std::string name) -> const Value*; // Access to alternatives auto GetIntValue() const -> int; @@ -215,6 +225,7 @@ struct Value { auto GetPointerType() const -> const PointerType&; auto GetStructType() const -> const StructType&; auto GetChoiceType() const -> const ChoiceType&; + auto GetVariableType() const -> const VariableType&; auto GetContinuationValue() const -> const ContinuationValue&; inline auto tag() const -> ValKind { @@ -236,7 +247,7 @@ struct Value { std::variant value; }; diff --git a/executable_semantics/syntax/BUILD b/executable_semantics/syntax/BUILD index 0cd2cd24c09f..f8551220342f 100644 --- a/executable_semantics/syntax/BUILD +++ b/executable_semantics/syntax/BUILD @@ -70,6 +70,7 @@ genrule( ], cmd = "M4=$(M4) $(BISON) " + "--output=$(location parser.cpp) " + + "--report=state " + "--defines=$(location parser.h) " + "$(location parser.ypp)", toolchains = [ diff --git a/executable_semantics/syntax/lexer.lpp b/executable_semantics/syntax/lexer.lpp index b23faea6e71f..09c651e760a5 100644 --- a/executable_semantics/syntax/lexer.lpp +++ b/executable_semantics/syntax/lexer.lpp @@ -135,6 +135,7 @@ operand_start [(A-Za-z0-9_"] "." return yy::parser::make_PERIOD(context.current_token_position); "," return yy::parser::make_COMMA(context.current_token_position); ";" return yy::parser::make_SEMICOLON(context.current_token_position); +":!" return yy::parser::make_COLON_BANG(context.current_token_position); ":" return yy::parser::make_COLON(context.current_token_position); /* diff --git a/executable_semantics/syntax/parser.ypp b/executable_semantics/syntax/parser.ypp index 58756de95fed..432e15601ced 100644 --- a/executable_semantics/syntax/parser.ypp +++ b/executable_semantics/syntax/parser.ypp @@ -98,6 +98,9 @@ void yy::parser::error(const location_type&, const std::string& message) { %type optional_else %type statement_list %type expression +%type generic_binding +%type > deduced_params +%type > deduced_param_list %type pattern %type return_type %type paren_expression @@ -167,12 +170,13 @@ void yy::parser::error(const location_type&, const std::string& message) { PERIOD "." COMMA "," SEMICOLON ";" + COLON_BANG ":!" COLON ":" ; %precedence FNARROW %precedence "{" "}" -%precedence ":" "," DBLARROW +%precedence ":!" ":" "," DBLARROW %left OR AND %nonassoc EQUAL_EQUAL %left "+" "-" @@ -372,20 +376,46 @@ return_type: | ARROW expression %prec FNARROW { $$ = $2; } ; +generic_binding: + identifier ":!" expression + { + $$ = Carbon::GenericBinding({.name = std::move($1), .type = $3}); + } +; +deduced_param_list: + // Empty + { $$ = std::vector(); } +| generic_binding + { + $$ = std::vector(); + $$.push_back($1); + } +| generic_binding "," deduced_param_list + { + $$ = $3; + $$.push_back($1); + } +; +deduced_params: + // Empty + { $$ = std::vector(); } +| "[" deduced_param_list "]" + { $$ = $2; } +; function_definition: - FN identifier tuple return_type "{" statement_list "}" - { $$ = Carbon::FunctionDefinition(yylineno, $2, $3, $4, $6); } -| FN identifier tuple DBLARROW expression ";" + FN identifier deduced_params tuple return_type "{" statement_list "}" + { $$ = Carbon::FunctionDefinition(yylineno, $2, $3, $4, $5, $7); } +| FN identifier deduced_params tuple DBLARROW expression ";" { $$ = Carbon::FunctionDefinition( - yylineno, $2, $3, + yylineno, $2, $3, $4, Carbon::Expression::MakeAutoTypeLiteral(yylineno), - Carbon::Statement::MakeReturn(yylineno, $5)); + Carbon::Statement::MakeReturn(yylineno, $6)); } ; function_declaration: - FN identifier tuple return_type ";" - { $$ = Carbon::FunctionDefinition(yylineno, $2, $3, $4, 0); } + FN identifier deduced_params tuple return_type ";" + { $$ = Carbon::FunctionDefinition(yylineno, $2, $3, $4, $5, 0); } ; variable_declaration: identifier ":" expression { $$ = Carbon::Member::MakeFieldMember(yylineno, $1, $3); } diff --git a/executable_semantics/testdata/generic_function1.carbon b/executable_semantics/testdata/generic_function1.carbon new file mode 100644 index 000000000000..3de4aba8bfee --- /dev/null +++ b/executable_semantics/testdata/generic_function1.carbon @@ -0,0 +1,11 @@ +// 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 id[T:! Type](x: T) -> T { + return x; +} + +fn main() -> Int { + return id(0); +} diff --git a/executable_semantics/testdata/generic_function1.golden b/executable_semantics/testdata/generic_function1.golden new file mode 100644 index 000000000000..2db2755da426 --- /dev/null +++ b/executable_semantics/testdata/generic_function1.golden @@ -0,0 +1 @@ +result: 0 diff --git a/executable_semantics/testdata/generic_function2.carbon b/executable_semantics/testdata/generic_function2.carbon new file mode 100644 index 000000000000..ef782e367647 --- /dev/null +++ b/executable_semantics/testdata/generic_function2.carbon @@ -0,0 +1,11 @@ +// 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 fst[T:! Type](x: T, y: T) -> T { + return x; +} + +fn main() -> Int { + return fst(0, 1); +} diff --git a/executable_semantics/testdata/generic_function2.golden b/executable_semantics/testdata/generic_function2.golden new file mode 100644 index 000000000000..2db2755da426 --- /dev/null +++ b/executable_semantics/testdata/generic_function2.golden @@ -0,0 +1 @@ +result: 0 diff --git a/executable_semantics/testdata/generic_function3.carbon b/executable_semantics/testdata/generic_function3.carbon new file mode 100644 index 000000000000..716139ef58dc --- /dev/null +++ b/executable_semantics/testdata/generic_function3.carbon @@ -0,0 +1,11 @@ +// 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 snd[T:! Type](x: Int, y: T) -> T { + return y; +} + +fn main() -> Int { + return snd(0, 1); +} diff --git a/executable_semantics/testdata/generic_function3.golden b/executable_semantics/testdata/generic_function3.golden new file mode 100644 index 000000000000..0c204052cb21 --- /dev/null +++ b/executable_semantics/testdata/generic_function3.golden @@ -0,0 +1 @@ +result: 1 diff --git a/executable_semantics/testdata/generic_function_apply.carbon b/executable_semantics/testdata/generic_function_apply.carbon new file mode 100644 index 000000000000..d88324ab34c7 --- /dev/null +++ b/executable_semantics/testdata/generic_function_apply.carbon @@ -0,0 +1,19 @@ +// 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 apply[T:! Type, U:! Type](f: fnty (T) -> U, x: T) -> U { + return f(x); +} + +fn positive(x: Bool) -> Int { + if (x) { + return 2; + } else { + return -2; + } +} + +fn main() -> Int { + return apply(positive, false); +} diff --git a/executable_semantics/testdata/generic_function_apply.golden b/executable_semantics/testdata/generic_function_apply.golden new file mode 100644 index 000000000000..49f08b507230 --- /dev/null +++ b/executable_semantics/testdata/generic_function_apply.golden @@ -0,0 +1 @@ +result: -2 diff --git a/executable_semantics/testdata/generic_function_fail1.carbon b/executable_semantics/testdata/generic_function_fail1.carbon new file mode 100644 index 000000000000..a21596bc4d98 --- /dev/null +++ b/executable_semantics/testdata/generic_function_fail1.carbon @@ -0,0 +1,11 @@ +// 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 fst[T:! Type](x: T, y: T) -> T { + return x; +} + +fn main() -> Int { + return fst(0, true); +} diff --git a/executable_semantics/testdata/generic_function_fail1.golden b/executable_semantics/testdata/generic_function_fail1.golden new file mode 100644 index 000000000000..ad1dbe684658 --- /dev/null +++ b/executable_semantics/testdata/generic_function_fail1.golden @@ -0,0 +1,4 @@ +ERROR: 10: type error in argument deduction +expected: Int +actual: Bool +EXIT CODE: 255 diff --git a/executable_semantics/testdata/generic_function_fail2.carbon b/executable_semantics/testdata/generic_function_fail2.carbon new file mode 100644 index 000000000000..690254d5d8da --- /dev/null +++ b/executable_semantics/testdata/generic_function_fail2.carbon @@ -0,0 +1,11 @@ +// 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 id[T:! Type](x: Int) -> Int { + return x; +} + +fn main() -> Int { + return id(0); +} diff --git a/executable_semantics/testdata/generic_function_fail2.golden b/executable_semantics/testdata/generic_function_fail2.golden new file mode 100644 index 000000000000..5d1817dfc71d --- /dev/null +++ b/executable_semantics/testdata/generic_function_fail2.golden @@ -0,0 +1,2 @@ +10: error, could not deduce type argument for type parameter T +EXIT CODE: 255 diff --git a/executable_semantics/testdata/generic_function_swap.carbon b/executable_semantics/testdata/generic_function_swap.carbon new file mode 100644 index 000000000000..b646b4a041f3 --- /dev/null +++ b/executable_semantics/testdata/generic_function_swap.carbon @@ -0,0 +1,11 @@ +// 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 swap[T:! Type, U:! Type](tuple: (T, U)) -> (U, T) { + return (tuple[1], tuple[0]); +} + +fn main() -> Int { + return swap((0, true))[1]; +} diff --git a/executable_semantics/testdata/generic_function_swap.golden b/executable_semantics/testdata/generic_function_swap.golden new file mode 100644 index 000000000000..2db2755da426 --- /dev/null +++ b/executable_semantics/testdata/generic_function_swap.golden @@ -0,0 +1 @@ +result: 0 diff --git a/executable_semantics/testdata/generic_function_tuple_map.carbon b/executable_semantics/testdata/generic_function_tuple_map.carbon new file mode 100644 index 000000000000..cd099cbd544d --- /dev/null +++ b/executable_semantics/testdata/generic_function_tuple_map.carbon @@ -0,0 +1,13 @@ +// 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 map[T:! Type](f: fnty (T) -> T, tuple: (T, T)) -> (T, T) { + return (f(tuple[0]), f(tuple[1])); +} + +fn inc(x: Int) -> Int { return x + 1; } + +fn main() -> Int { + return map(inc, (0, 2))[0]; +} diff --git a/executable_semantics/testdata/generic_function_tuple_map.golden b/executable_semantics/testdata/generic_function_tuple_map.golden new file mode 100644 index 000000000000..0c204052cb21 --- /dev/null +++ b/executable_semantics/testdata/generic_function_tuple_map.golden @@ -0,0 +1 @@ +result: 1