mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 21:30:12 +01:00
Add extensibility support for arithmetic operators. (#1751)
Add new prelude interfaces `Negate`, `AddWith`, `SubWith`, and `MulWith`, and use them to implement non-builtin arithmetic operations.
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -12,11 +12,17 @@ namespace Carbon {
|
||||
|
||||
void Builtins::Register(Nonnull<const Declaration*> decl) {
|
||||
if (auto* interface = dyn_cast<InterfaceDeclaration>(decl)) {
|
||||
if (interface->name() == GetName(Builtin::ImplicitAs)) {
|
||||
builtins_[static_cast<int>(Builtin::ImplicitAs)] = interface;
|
||||
}
|
||||
if (interface->name() == GetName(Builtin::As)) {
|
||||
builtins_[static_cast<int>(Builtin::As)] = interface;
|
||||
static std::map<std::string, int>* builtin_indexes = [] {
|
||||
std::map<std::string, int> builtin_indexes;
|
||||
for (int index = 0; index <= static_cast<int>(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<const Declaration*> decl);
|
||||
@@ -39,7 +55,8 @@ class Builtins {
|
||||
|
||||
private:
|
||||
static constexpr int NumBuiltins = static_cast<int>(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<Nonnull<const Declaration*>> builtins_[NumBuiltins] = {};
|
||||
};
|
||||
|
||||
@@ -127,14 +127,20 @@ static void SetValue(Nonnull<Pattern*> pattern, Nonnull<const Value*> value) {
|
||||
}
|
||||
}
|
||||
|
||||
auto TypeChecker::IsSameType(Nonnull<const Value*> type1,
|
||||
Nonnull<const Value*> 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<const Value*> expected,
|
||||
Nonnull<const Value*> actual,
|
||||
const ImplScope& impl_scope) const
|
||||
-> ErrorOr<Success> {
|
||||
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<Expression*> e,
|
||||
CARBON_RETURN_IF_ERROR(TypeCheckExp(argument, impl_scope));
|
||||
ts.push_back(&argument->static_type());
|
||||
}
|
||||
|
||||
auto handle_binary_arithmetic =
|
||||
[&](Builtins::Builtin builtin) -> ErrorOr<Success> {
|
||||
// Handle a built-in operator first.
|
||||
if (isa<IntType>(ts[0]) && isa<IntType>(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<Nonnull<Expression*>> 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<IntType>(), ts[0],
|
||||
impl_scope));
|
||||
op.set_static_type(arena_->New<IntType>());
|
||||
op.set_value_category(ValueCategory::Let);
|
||||
case Operator::Neg: {
|
||||
// Handle a built-in negation first.
|
||||
if (isa<IntType>(ts[0])) {
|
||||
op.set_static_type(arena_->New<IntType>());
|
||||
op.set_value_category(ValueCategory::Let);
|
||||
return Success();
|
||||
}
|
||||
// Now try an overloaded negation.
|
||||
ErrorOr<Nonnull<Expression*>> 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<IntType>(), ts[0],
|
||||
impl_scope));
|
||||
CARBON_RETURN_IF_ERROR(ExpectExactType(e->source_loc(), "addition(2)",
|
||||
arena_->New<IntType>(), ts[1],
|
||||
impl_scope));
|
||||
op.set_static_type(arena_->New<IntType>());
|
||||
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<IntType>(), ts[0], impl_scope));
|
||||
CARBON_RETURN_IF_ERROR(
|
||||
ExpectExactType(e->source_loc(), "subtraction(2)",
|
||||
arena_->New<IntType>(), ts[1], impl_scope));
|
||||
op.set_static_type(arena_->New<IntType>());
|
||||
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<IntType>(), ts[0], impl_scope));
|
||||
CARBON_RETURN_IF_ERROR(
|
||||
ExpectExactType(e->source_loc(), "multiplication(2)",
|
||||
arena_->New<IntType>(), ts[1], impl_scope));
|
||||
op.set_static_type(arena_->New<IntType>());
|
||||
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<BoolType>(), ts[0],
|
||||
@@ -2812,9 +2831,8 @@ auto TypeChecker::TypeCheckStmt(Nonnull<Statement*> 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 `"
|
||||
|
||||
@@ -328,6 +328,13 @@ class TypeChecker {
|
||||
Nonnull<const Value*> destination)
|
||||
-> ErrorOr<Nonnull<Expression*>>;
|
||||
|
||||
// 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<const Value*> type1, Nonnull<const Value*> 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.
|
||||
//
|
||||
|
||||
+4
-3
@@ -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) {
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+24
@@ -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;
|
||||
}
|
||||
+19
@@ -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);
|
||||
}
|
||||
+21
@@ -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;
|
||||
}
|
||||
+21
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
+21
@@ -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;
|
||||
}
|
||||
+24
@@ -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;
|
||||
}
|
||||
+19
@@ -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);
|
||||
}
|
||||
+24
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
+24
@@ -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;
|
||||
}
|
||||
+19
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user