diff --git a/explorer/data/prelude.carbon b/explorer/data/prelude.carbon index e06202efaa62..828c04d6b2db 100644 --- a/explorer/data/prelude.carbon +++ b/explorer/data/prelude.carbon @@ -49,6 +49,47 @@ impl forall [U1:! Type, U2:! Type, U3:! Type, } } +interface Negate { + // TODO: = Self + let Result:! Type; + fn Op[me: Self]() -> Result; +} + +interface AddWith(U:! Type) { + // TODO: = Self + let Result:! Type; + fn Op[me: Self](other: U) -> Result; +} +// TODO: constraint Add { ... } + +interface SubWith(U:! Type) { + // TODO: = Self + let Result:! Type; + fn Op[me: Self](other: U) -> Result; +} +// TODO: constraint Sub { ... } + +interface MulWith(U:! Type) { + // TODO: = Self + let Result:! Type; + fn Op[me: Self](other: U) -> Result; +} +// TODO: constraint Mul { ... } + +// Note, these impls use the builtin addition for i32. +external impl i32 as Negate where .Result == i32 { + fn Op[me: i32]() -> i32 { return -me; } +} +external impl i32 as AddWith(i32) where .Result == i32 { + fn Op[me: i32](other: i32) -> i32 { return me + other; } +} +external impl i32 as SubWith(i32) where .Result == i32 { + fn Op[me: i32](other: i32) -> i32 { return me - other; } +} +external impl i32 as MulWith(i32) where .Result == i32 { + fn Op[me: i32](other: i32) -> i32 { return me * other; } +} + // Note that Print is experimental, and not part of an accepted proposal, but // is included here for printing state in tests. // TODO: Remove Print special casing once we have variadics or overloads. diff --git a/explorer/interpreter/builtins.cpp b/explorer/interpreter/builtins.cpp index 5f2b0d909db2..0b343730ce00 100644 --- a/explorer/interpreter/builtins.cpp +++ b/explorer/interpreter/builtins.cpp @@ -12,11 +12,17 @@ namespace Carbon { void Builtins::Register(Nonnull decl) { if (auto* interface = dyn_cast(decl)) { - if (interface->name() == GetName(Builtin::ImplicitAs)) { - builtins_[static_cast(Builtin::ImplicitAs)] = interface; - } - if (interface->name() == GetName(Builtin::As)) { - builtins_[static_cast(Builtin::As)] = interface; + static std::map* builtin_indexes = [] { + std::map builtin_indexes; + for (int index = 0; index <= static_cast(Builtin::Last); ++index) { + builtin_indexes.emplace(BuiltinNames[index], index); + } + return new auto(std::move(builtin_indexes)); + }(); + + auto it = builtin_indexes->find(interface->name()); + if (it != builtin_indexes->end()) { + builtins_[it->second] = interface; } } } diff --git a/explorer/interpreter/builtins.h b/explorer/interpreter/builtins.h index 235fc7b80475..525955289ceb 100644 --- a/explorer/interpreter/builtins.h +++ b/explorer/interpreter/builtins.h @@ -20,10 +20,26 @@ class Builtins { public: explicit Builtins() {} - enum class Builtin { ImplicitAs, As, Last = As }; + enum class Builtin { + // Conversions. + As, + ImplicitAs, + + // Arithmetic. + Negate, + AddWith, + SubWith, + MulWith, + + Last = MulWith + }; // TODO: In C++20, replace with `using enum Builtin;`. - static constexpr Builtin ImplicitAs = Builtin::ImplicitAs; static constexpr Builtin As = Builtin::As; + static constexpr Builtin ImplicitAs = Builtin::ImplicitAs; + static constexpr Builtin Negate = Builtin::Negate; + static constexpr Builtin AddWith = Builtin::AddWith; + static constexpr Builtin SubWith = Builtin::SubWith; + static constexpr Builtin MulWith = Builtin::MulWith; // Register a declaration that might be a builtin. void Register(Nonnull decl); @@ -39,7 +55,8 @@ class Builtins { private: static constexpr int NumBuiltins = static_cast(Builtin::Last) + 1; - static constexpr const char* BuiltinNames[NumBuiltins] = {"ImplicitAs", "As"}; + static constexpr const char* BuiltinNames[NumBuiltins] = { + "As", "ImplicitAs", "Negate", "AddWith", "SubWith", "MulWith"}; std::optional> builtins_[NumBuiltins] = {}; }; diff --git a/explorer/interpreter/type_checker.cpp b/explorer/interpreter/type_checker.cpp index 804c6e2ecf0c..9c02cf12a8a3 100644 --- a/explorer/interpreter/type_checker.cpp +++ b/explorer/interpreter/type_checker.cpp @@ -127,14 +127,20 @@ static void SetValue(Nonnull pattern, Nonnull value) { } } +auto TypeChecker::IsSameType(Nonnull type1, + Nonnull type2, + const ImplScope& impl_scope) const -> bool { + SingleStepEqualityContext equality_ctx(this, &impl_scope); + return TypeEqual(type1, type2, &equality_ctx); +} + auto TypeChecker::ExpectExactType(SourceLocation source_loc, const std::string& context, Nonnull expected, Nonnull actual, const ImplScope& impl_scope) const -> ErrorOr { - SingleStepEqualityContext equality_ctx(this, &impl_scope); - if (!TypeEqual(expected, actual, &equality_ctx)) { + if (!IsSameType(expected, actual, impl_scope)) { return CompilationError(source_loc) << "type error in " << context << "\n" << "expected: " << *expected << "\n" << "actual: " << *actual; @@ -380,8 +386,7 @@ auto TypeChecker::IsImplicitlyConvertible( // conversions. CARBON_CHECK(IsConcreteType(source)); CARBON_CHECK(IsConcreteType(destination)); - SingleStepEqualityContext equality_ctx(this, &impl_scope); - if (TypeEqual(source, destination, &equality_ctx)) { + if (IsSameType(source, destination, impl_scope)) { return true; } @@ -1938,44 +1943,58 @@ auto TypeChecker::TypeCheckExp(Nonnull e, CARBON_RETURN_IF_ERROR(TypeCheckExp(argument, impl_scope)); ts.push_back(&argument->static_type()); } + + auto handle_binary_arithmetic = + [&](Builtins::Builtin builtin) -> ErrorOr { + // Handle a built-in operator first. + if (isa(ts[0]) && isa(ts[1]) && + IsSameType(ts[0], ts[1], impl_scope)) { + op.set_static_type(ts[0]); + op.set_value_category(ValueCategory::Let); + return Success(); + } + + // Now try an overloaded operator. + ErrorOr> result = BuildBuiltinMethodCall( + impl_scope, op.arguments()[0], BuiltinInterfaceName{builtin, ts[1]}, + BuiltinMethodCall{"Op", {op.arguments()[1]}}); + if (!result.ok()) { + // We couldn't find a matching `impl`. + return CompilationError(e->source_loc()) + << "type error in `" << ToString(op.op()) << "`:\n" + << result.error().message(); + } + op.set_rewritten_form(*result); + return Success(); + }; + switch (op.op()) { - case Operator::Neg: - CARBON_RETURN_IF_ERROR(ExpectExactType(e->source_loc(), "negation", - arena_->New(), ts[0], - impl_scope)); - op.set_static_type(arena_->New()); - op.set_value_category(ValueCategory::Let); + case Operator::Neg: { + // Handle a built-in negation first. + if (isa(ts[0])) { + op.set_static_type(arena_->New()); + op.set_value_category(ValueCategory::Let); + return Success(); + } + // Now try an overloaded negation. + ErrorOr> result = BuildBuiltinMethodCall( + impl_scope, op.arguments()[0], + BuiltinInterfaceName{Builtins::Negate}, BuiltinMethodCall{"Op"}); + if (!result.ok()) { + // We couldn't find a matching `impl`. + return CompilationError(e->source_loc()) + << "type error in `" << ToString(op.op()) << "`:\n" + << result.error().message(); + } + op.set_rewritten_form(*result); return Success(); + } case Operator::Add: - CARBON_RETURN_IF_ERROR(ExpectExactType(e->source_loc(), "addition(1)", - arena_->New(), ts[0], - impl_scope)); - CARBON_RETURN_IF_ERROR(ExpectExactType(e->source_loc(), "addition(2)", - arena_->New(), ts[1], - impl_scope)); - op.set_static_type(arena_->New()); - op.set_value_category(ValueCategory::Let); - return Success(); + return handle_binary_arithmetic(Builtins::AddWith); case Operator::Sub: - CARBON_RETURN_IF_ERROR( - ExpectExactType(e->source_loc(), "subtraction(1)", - arena_->New(), ts[0], impl_scope)); - CARBON_RETURN_IF_ERROR( - ExpectExactType(e->source_loc(), "subtraction(2)", - arena_->New(), ts[1], impl_scope)); - op.set_static_type(arena_->New()); - op.set_value_category(ValueCategory::Let); - return Success(); + return handle_binary_arithmetic(Builtins::SubWith); case Operator::Mul: - CARBON_RETURN_IF_ERROR( - ExpectExactType(e->source_loc(), "multiplication(1)", - arena_->New(), ts[0], impl_scope)); - CARBON_RETURN_IF_ERROR( - ExpectExactType(e->source_loc(), "multiplication(2)", - arena_->New(), ts[1], impl_scope)); - op.set_static_type(arena_->New()); - op.set_value_category(ValueCategory::Let); - return Success(); + return handle_binary_arithmetic(Builtins::MulWith); case Operator::And: CARBON_RETURN_IF_ERROR(ExpectExactType(e->source_loc(), "&&(1)", arena_->New(), ts[0], @@ -2812,9 +2831,8 @@ auto TypeChecker::TypeCheckStmt(Nonnull s, // TODO: Consider using `ExpectExactType` here. CARBON_CHECK(IsConcreteType(&return_term.static_type())); CARBON_CHECK(IsConcreteType(&ret.value_node().static_type())); - SingleStepEqualityContext equality_ctx(this, &impl_scope); - if (!TypeEqual(&return_term.static_type(), - &ret.value_node().static_type(), &equality_ctx)) { + if (!IsSameType(&return_term.static_type(), + &ret.value_node().static_type(), impl_scope)) { return CompilationError(ret.value_node().base().source_loc()) << "type of returned var `" << ret.value_node().static_type() << "` does not match return type `" diff --git a/explorer/interpreter/type_checker.h b/explorer/interpreter/type_checker.h index 78ded625aa70..c1530340da19 100644 --- a/explorer/interpreter/type_checker.h +++ b/explorer/interpreter/type_checker.h @@ -328,6 +328,13 @@ class TypeChecker { Nonnull destination) -> ErrorOr>; + // Determine whether `type1` and `type2` are considered to be the same type + // in the given scope. This is true if they're structurally identical or if + // there is an equality relation in scope that specifies that they are the + // same. + auto IsSameType(Nonnull type1, Nonnull type2, + const ImplScope& impl_scope) const -> bool; + // Check whether `actual` is implicitly convertible to `expected` // and halt with a fatal compilation error if it is not. // diff --git a/explorer/testdata/function/fail_invalid_fnty.carbon b/explorer/testdata/function/fail_invalid_fnty.carbon index 86aa72942301..f3e9e38ceaf1 100644 --- a/explorer/testdata/function/fail_invalid_fnty.carbon +++ b/explorer/testdata/function/fail_invalid_fnty.carbon @@ -10,9 +10,10 @@ package ExplorerTest api; -// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/function/fail_invalid_fnty.carbon:[[@LINE+3]]: type error in negation -// CHECK: expected: i32 -// CHECK: actual: Bool +// TODO: We should type-check the prelude before the main input so that we can +// use it within the types of top-level declarations. +// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/function/fail_invalid_fnty.carbon:[[@LINE+2]]: type error in `-`: +// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/function/fail_invalid_fnty.carbon:[[@LINE+1]]: missing declaration for builtin `Negate` fn f(g: __Fn(-true) -> true) { } diff --git a/explorer/testdata/generic_function/fail_not_addable.carbon b/explorer/testdata/generic_function/fail_not_addable.carbon index 48f3d54a6bbc..6f8e8c6deae9 100644 --- a/explorer/testdata/generic_function/fail_not_addable.carbon +++ b/explorer/testdata/generic_function/fail_not_addable.carbon @@ -11,9 +11,8 @@ package ExplorerTest api; fn id[T:! Type](x: T) -> T { - // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/generic_function/fail_not_addable.carbon:[[@LINE+3]]: type error in addition(1) - // CHECK: expected: i32 - // CHECK: actual: T:! Type + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/generic_function/fail_not_addable.carbon:[[@LINE+2]]: type error in `+`: + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/generic_function/fail_not_addable.carbon:[[@LINE+1]]: could not find implementation of interface AddWith(U = i32) for T:! Type return x + 0; } diff --git a/explorer/testdata/interface/impl_self_interface_parameter.carbon b/explorer/testdata/interface/impl_self_interface_parameter.carbon index 985dfbfc0977..24285f26432f 100644 --- a/explorer/testdata/interface/impl_self_interface_parameter.carbon +++ b/explorer/testdata/interface/impl_self_interface_parameter.carbon @@ -11,7 +11,7 @@ package ExplorerTest api; -interface AddWith(T:! Type) { +interface MyAddWith(T:! Type) { fn Op[me: Self](b: T) -> Self; } @@ -21,13 +21,13 @@ class Point { } // Allowed: `Self` means `Point` after `as` -impl Point as AddWith(Self) { +impl Point as MyAddWith(Self) { fn Op[me: Point](b: Point) -> Point { return {.x = me.x + b.x, .y = me.y + b.y}; } } -fn DoAddGeneric[T:! Type, U:! AddWith(T)](a: U, b: T) -> U { +fn DoAddGeneric[T:! Type, U:! MyAddWith(T)](a: U, b: T) -> U { return a.Op(b); } diff --git a/explorer/testdata/operators/add.carbon b/explorer/testdata/operators/add.carbon new file mode 100644 index 000000000000..6bf042244704 --- /dev/null +++ b/explorer/testdata/operators/add.carbon @@ -0,0 +1,24 @@ +// 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 +// +// RUN: %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s +// CHECK: result: 6 + +package ExplorerTest api; + +class A { var n: i32; } + +external impl A as AddWith(i32) where .Result == A { + fn Op[me: Self](rhs: i32) -> A { return {.n = me.n + rhs}; } +} + +fn Main() -> i32 { + var a: A = {.n = 5}; + a = a + 1; + return a.n; +} diff --git a/explorer/testdata/operators/add_builtin.carbon b/explorer/testdata/operators/add_builtin.carbon new file mode 100644 index 000000000000..7a68276a6121 --- /dev/null +++ b/explorer/testdata/operators/add_builtin.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 +// +// RUN: %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s +// CHECK: result: 3 + +package ExplorerTest api; + +// TODO: T:! Add +fn DoAdd[T:! AddWith(.Self) where .Result == .Self](x: T, y: T) -> T { return x + y; } + +fn Main() -> i32 { + return DoAdd(1, 2); +} diff --git a/explorer/testdata/operators/fail_no_add.carbon b/explorer/testdata/operators/fail_no_add.carbon new file mode 100644 index 000000000000..ebda08226692 --- /dev/null +++ b/explorer/testdata/operators/fail_no_add.carbon @@ -0,0 +1,21 @@ +// 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 +// +// RUN: %{not} %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s + +package ExplorerTest api; + +class A {} + +fn Main() -> i32 { + var a: A = {}; + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/operators/fail_no_add.carbon:[[@LINE+2]]: type error in `+`: + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/operators/fail_no_add.carbon:[[@LINE+1]]: could not find implementation of interface AddWith(U = i32) for class A + a + 1; + return 0; +} diff --git a/explorer/testdata/operators/fail_no_mul.carbon b/explorer/testdata/operators/fail_no_mul.carbon new file mode 100644 index 000000000000..ac5e3c931d26 --- /dev/null +++ b/explorer/testdata/operators/fail_no_mul.carbon @@ -0,0 +1,21 @@ +// 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 +// +// RUN: %{not} %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s + +package ExplorerTest api; + +class A {} + +fn Main() -> i32 { + var a: A = {}; + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/operators/fail_no_mul.carbon:[[@LINE+2]]: type error in `*`: + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/operators/fail_no_mul.carbon:[[@LINE+1]]: could not find implementation of interface MulWith(U = i32) for class A + a * 1; + return 0; +} diff --git a/explorer/testdata/operators/fail_no_negate.carbon b/explorer/testdata/operators/fail_no_negate.carbon new file mode 100644 index 000000000000..892f59e6aa36 --- /dev/null +++ b/explorer/testdata/operators/fail_no_negate.carbon @@ -0,0 +1,21 @@ +// 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 +// +// RUN: %{not} %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s + +package ExplorerTest api; + +class A {} + +fn Main() -> i32 { + var a: A = {}; + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/operators/fail_no_negate.carbon:[[@LINE+2]]: type error in `-`: + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/operators/fail_no_negate.carbon:[[@LINE+1]]: could not find implementation of interface Negate for class A + -a; + return 0; +} diff --git a/explorer/testdata/operators/fail_no_sub.carbon b/explorer/testdata/operators/fail_no_sub.carbon new file mode 100644 index 000000000000..91455b8cf896 --- /dev/null +++ b/explorer/testdata/operators/fail_no_sub.carbon @@ -0,0 +1,21 @@ +// 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 +// +// RUN: %{not} %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s + +package ExplorerTest api; + +class A {} + +fn Main() -> i32 { + var a: A = {}; + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/operators/fail_no_sub.carbon:[[@LINE+2]]: type error in `-`: + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/operators/fail_no_sub.carbon:[[@LINE+1]]: could not find implementation of interface SubWith(U = i32) for class A + a - 1; + return 0; +} diff --git a/explorer/testdata/operators/mul.carbon b/explorer/testdata/operators/mul.carbon new file mode 100644 index 000000000000..a9c89c4f93f8 --- /dev/null +++ b/explorer/testdata/operators/mul.carbon @@ -0,0 +1,24 @@ +// 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 +// +// RUN: %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s +// CHECK: result: 10 + +package ExplorerTest api; + +class A { var n: i32; } + +external impl A as MulWith(i32) where .Result == A { + fn Op[me: Self](rhs: i32) -> A { return {.n = me.n * rhs}; } +} + +fn Main() -> i32 { + var a: A = {.n = 5}; + a = a * 2; + return a.n; +} diff --git a/explorer/testdata/operators/mul_builtin.carbon b/explorer/testdata/operators/mul_builtin.carbon new file mode 100644 index 000000000000..2df8512560c4 --- /dev/null +++ b/explorer/testdata/operators/mul_builtin.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 +// +// RUN: %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s +// CHECK: result: 6 + +package ExplorerTest api; + +// TODO: T:! Mul +fn DoMul[T:! MulWith(.Self) where .Result == .Self](x: T, y: T) -> T { return x * y; } + +fn Main() -> i32 { + return DoMul(2, 3); +} diff --git a/explorer/testdata/operators/negate.carbon b/explorer/testdata/operators/negate.carbon new file mode 100644 index 000000000000..2b2a32c93421 --- /dev/null +++ b/explorer/testdata/operators/negate.carbon @@ -0,0 +1,24 @@ +// 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 +// +// RUN: %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s +// CHECK: result: -5 + +package ExplorerTest api; + +class A { var n: i32; } + +external impl A as Negate where .Result == A { + fn Op[me: Self]() -> A { return {.n = -me.n}; } +} + +fn Main() -> i32 { + var a: A = {.n = 5}; + a = -a; + return a.n; +} diff --git a/explorer/testdata/operators/negate_builtin.carbon b/explorer/testdata/operators/negate_builtin.carbon new file mode 100644 index 000000000000..bf8883e7a27f --- /dev/null +++ b/explorer/testdata/operators/negate_builtin.carbon @@ -0,0 +1,18 @@ +// 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 +// +// RUN: %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s +// CHECK: result: -3 + +package ExplorerTest api; + +fn DoNegate[T:! Negate where .Result == .Self](x: T) -> T { return -x; } + +fn Main() -> i32 { + return DoNegate(3); +} diff --git a/explorer/testdata/operators/sub.carbon b/explorer/testdata/operators/sub.carbon new file mode 100644 index 000000000000..f9ed3a4438e4 --- /dev/null +++ b/explorer/testdata/operators/sub.carbon @@ -0,0 +1,24 @@ +// 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 +// +// RUN: %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s +// CHECK: result: 4 + +package ExplorerTest api; + +class A { var n: i32; } + +external impl A as SubWith(i32) where .Result == A { + fn Op[me: Self](rhs: i32) -> A { return {.n = me.n - rhs}; } +} + +fn Main() -> i32 { + var a: A = {.n = 5}; + a = a - 1; + return a.n; +} diff --git a/explorer/testdata/operators/sub_builtin.carbon b/explorer/testdata/operators/sub_builtin.carbon new file mode 100644 index 000000000000..181102d7281f --- /dev/null +++ b/explorer/testdata/operators/sub_builtin.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 +// +// RUN: %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s +// CHECK: result: 1 + +package ExplorerTest api; + +// TODO: T:! Sub +fn DoSub[T:! SubWith(.Self) where .Result == .Self](x: T, y: T) -> T { return x - y; } + +fn Main() -> i32 { + return DoSub(3, 2); +}