diff --git a/common/fuzzing/carbon.proto b/common/fuzzing/carbon.proto index bf6ef9e6f35b..a6ab747e17b4 100644 --- a/common/fuzzing/carbon.proto +++ b/common/fuzzing/carbon.proto @@ -52,9 +52,14 @@ message OperatorExpression { Or = 9; Sub = 10; Ptr = 11; - Combine = 12; + BitwiseAnd = 12; As = 13; Mod = 14; + Complement = 15; + BitwiseOr = 16; + BitwiseXor = 17; + BitShiftLeft = 18; + BitShiftRight = 19; } optional Operator op = 1; repeated Expression arguments = 2; @@ -85,20 +90,6 @@ message DesignatorExpression { optional string name = 1; } -message IntrinsicExpression { - enum Intrinsic { - UnknownIntrinsic = 0; - Print = 1; - Alloc = 2; - Dealloc = 3; - Rand = 4; - IntEq = 5; - StrEq = 6; - } - optional Intrinsic intrinsic = 1; - optional TupleLiteralExpression argument = 2; -} - message IfExpression { optional Expression condition = 1; optional Expression then_expression = 2; @@ -165,7 +156,6 @@ message Expression { StructLiteralExpression struct_literal = 7; StructTypeLiteralExpression struct_type_literal = 8; IdentifierExpression identifier = 9; - IntrinsicExpression intrinsic = 10; IfExpression if_expression = 11; BoolTypeLiteral bool_type_literal = 12; BoolLiteral bool_literal = 13; diff --git a/common/fuzzing/proto_to_carbon.cpp b/common/fuzzing/proto_to_carbon.cpp index 5d69a87037ec..6f9caf64a77a 100644 --- a/common/fuzzing/proto_to_carbon.cpp +++ b/common/fuzzing/proto_to_carbon.cpp @@ -154,9 +154,29 @@ static auto OperatorToCarbon(const Fuzzing::OperatorExpression& operator_expr, BinaryOperatorToCarbon(arg0, " or ", arg1, out); break; - case Fuzzing::OperatorExpression::Combine: + case Fuzzing::OperatorExpression::Complement: + PrefixUnaryOperatorToCarbon("^", arg0, out); + break; + + case Fuzzing::OperatorExpression::BitwiseAnd: BinaryOperatorToCarbon(arg0, " & ", arg1, out); break; + + case Fuzzing::OperatorExpression::BitwiseOr: + BinaryOperatorToCarbon(arg0, " | ", arg1, out); + break; + + case Fuzzing::OperatorExpression::BitwiseXor: + BinaryOperatorToCarbon(arg0, " ^ ", arg1, out); + break; + + case Fuzzing::OperatorExpression::BitShiftLeft: + BinaryOperatorToCarbon(arg0, " << ", arg1, out); + break; + + case Fuzzing::OperatorExpression::BitShiftRight: + BinaryOperatorToCarbon(arg0, " >> ", arg1, out); + break; } out << ")"; } @@ -289,36 +309,6 @@ static auto ExpressionToCarbon(const Fuzzing::Expression& expression, break; } - case Fuzzing::Expression::kIntrinsic: { - const auto& intrinsic = expression.intrinsic(); - switch (intrinsic.intrinsic()) { - case Fuzzing::IntrinsicExpression::UnknownIntrinsic: - // Arbitrary default to avoid getting invalid syntax. - out << "__intrinsic_print"; - break; - - case Fuzzing::IntrinsicExpression::Print: - out << "__intrinsic_print"; - break; - case Fuzzing::IntrinsicExpression::Alloc: - out << "__intrinsic_new"; - break; - case Fuzzing::IntrinsicExpression::Dealloc: - out << "__intrinsic_delete"; - break; - case Fuzzing::IntrinsicExpression::Rand: - out << "__intrinsic_rand"; - break; - case Fuzzing::IntrinsicExpression::IntEq: - out << "__intrinsic_int_eq"; - break; - case Fuzzing::IntrinsicExpression::StrEq: - out << "__intrinsic_str_eq"; - break; - } - TupleLiteralExpressionToCarbon(intrinsic.argument(), out); - } break; - case Fuzzing::Expression::kIfExpression: { const auto& if_expression = expression.if_expression(); out << "if "; diff --git a/explorer/ast/expression.cpp b/explorer/ast/expression.cpp index 25845ae33001..0a1cbcc640f1 100644 --- a/explorer/ast/expression.cpp +++ b/explorer/ast/expression.cpp @@ -32,6 +32,12 @@ auto IntrinsicExpression::FindIntrinsic(std::string_view name, {"delete", Intrinsic::Dealloc}, {"rand", Intrinsic::Rand}, {"int_eq", Intrinsic::IntEq}, + {"int_bit_complement", Intrinsic::IntBitComplement}, + {"int_bit_and", Intrinsic::IntBitAnd}, + {"int_bit_or", Intrinsic::IntBitOr}, + {"int_bit_xor", Intrinsic::IntBitXor}, + {"int_left_shift", Intrinsic::IntLeftShift}, + {"int_right_shift", Intrinsic::IntRightShift}, {"str_eq", Intrinsic::StrEq}}); name.remove_prefix(std::strlen("__intrinsic_")); auto it = intrinsic_map.find(name); @@ -41,6 +47,36 @@ auto IntrinsicExpression::FindIntrinsic(std::string_view name, return it->second; } +auto IntrinsicExpression::name() const -> std::string_view { + switch (intrinsic()) { + case IntrinsicExpression::Intrinsic::Print: + // TODO: Remove Print special casing once we have variadics or overloads. + return "Print"; + case IntrinsicExpression::Intrinsic::Alloc: + return "__intrinsic_new"; + case IntrinsicExpression::Intrinsic::Dealloc: + return "__intrinsic_delete"; + case IntrinsicExpression::Intrinsic::Rand: + return "__intrinsic_rand"; + case IntrinsicExpression::Intrinsic::IntEq: + return "__intrinsic_int_eq"; + case IntrinsicExpression::Intrinsic::IntBitComplement: + return "__intrinsic_int_bit_complement"; + case IntrinsicExpression::Intrinsic::IntBitAnd: + return "__intrinsic_int_bit_and"; + case IntrinsicExpression::Intrinsic::IntBitOr: + return "__intrinsic_int_bit_or"; + case IntrinsicExpression::Intrinsic::IntBitXor: + return "__intrinsic_int_bit_xor"; + case IntrinsicExpression::Intrinsic::IntLeftShift: + return "__intrinsic_int_left_shift"; + case IntrinsicExpression::Intrinsic::IntRightShift: + return "__intrinsic_int_right_shift"; + case IntrinsicExpression::Intrinsic::StrEq: + return "__intrinsic_str_eq"; + } +} + auto ExpressionFromParenContents( Nonnull arena, SourceLocation source_loc, const ParenContents& paren_contents) -> Nonnull { @@ -67,8 +103,17 @@ auto ToString(Operator op) -> std::string_view { case Operator::As: return "as"; case Operator::AddressOf: - case Operator::Combine: + case Operator::BitwiseAnd: return "&"; + case Operator::BitwiseOr: + return "|"; + case Operator::BitwiseXor: + case Operator::Complement: + return "^"; + case Operator::BitShiftLeft: + return "<<"; + case Operator::BitShiftRight: + return ">>"; case Operator::Neg: case Operator::Sub: return "-"; @@ -173,33 +218,7 @@ void Expression::Print(llvm::raw_ostream& out) const { } case ExpressionKind::IntrinsicExpression: { const auto& iexp = cast(*this); - // TODO: Remove Print special casing once we have variadics or overloads. - if (iexp.intrinsic() == IntrinsicExpression::Intrinsic::Print) { - out << "Print" << iexp.args(); - break; - } - - out << "intrinsic_"; - switch (iexp.intrinsic()) { - case IntrinsicExpression::Intrinsic::Print: - out << "print"; - break; - case IntrinsicExpression::Intrinsic::Alloc: - out << "new"; - break; - case IntrinsicExpression::Intrinsic::Dealloc: - out << "delete"; - break; - case IntrinsicExpression::Intrinsic::Rand: - out << "rand"; - break; - case IntrinsicExpression::Intrinsic::IntEq: - out << "int_eq"; - break; - case IntrinsicExpression::Intrinsic::StrEq: - out << "str_eq"; - } - out << iexp.args(); + out << iexp.name() << iexp.args(); break; } case ExpressionKind::IfExpression: { diff --git a/explorer/ast/expression.h b/explorer/ast/expression.h index a2e4ce79d091..14e1ed1e0f7b 100644 --- a/explorer/ast/expression.h +++ b/explorer/ast/expression.h @@ -116,7 +116,12 @@ enum class Operator { AddressOf, And, As, - Combine, + BitwiseAnd, + BitwiseOr, + BitwiseXor, + BitShiftLeft, + BitShiftRight, + Complement, Deref, Eq, Mul, @@ -664,7 +669,20 @@ class ValueLiteral : public Expression { class IntrinsicExpression : public Expression { public: - enum class Intrinsic { Print, Alloc, Dealloc, Rand, IntEq, StrEq }; + enum class Intrinsic { + Print, + Alloc, + Dealloc, + Rand, + IntEq, + StrEq, + IntBitAnd, + IntBitOr, + IntBitXor, + IntBitComplement, + IntLeftShift, + IntRightShift, + }; // Returns the enumerator corresponding to the intrinsic named `name`, // or raises a fatal compile error if there is no such enumerator. @@ -682,6 +700,7 @@ class IntrinsicExpression : public Expression { } auto intrinsic() const -> Intrinsic { return intrinsic_; } + auto name() const -> std::string_view; auto args() const -> const TupleLiteral& { return *args_; } auto args() -> TupleLiteral& { return *args_; } diff --git a/explorer/data/prelude.carbon b/explorer/data/prelude.carbon index 92bc588d0a7e..59cfe6a26fcd 100644 --- a/explorer/data/prelude.carbon +++ b/explorer/data/prelude.carbon @@ -4,6 +4,10 @@ package Carbon api; +// ---------------------- +// Conversion interfaces. +// ---------------------- + // Explicitly convert `Self` to `T`. interface As(T:! Type) { fn Convert[me: Self]() -> T; @@ -49,6 +53,43 @@ impl forall [U1:! Type, U2:! Type, U3:! Type, } } +// ---------------------- +// Comparison interfaces. +// ---------------------- + +interface EqWith(U:! Type) { + fn Equal[me: Self](other: U) -> Bool; + // TODO: NotEqual with default impl +} +// TODO: constraint Eq { ... } + + +// TODO: Simplify this once we have variadics +impl forall [T2:! Type, U2:! Type, T1:! EqWith(T2), U1:! EqWith(U2)] + (T1, U1) as EqWith((T2, U2)) { + fn Equal[me: Self](other: (T2, U2)) -> Bool { + let (l1: T1, l2: U1) = me; + let (r1: T2, r2: U2) = other; + return l1 == r1 and l2 == r2; + } +} + +impl i32 as EqWith(Self) { + fn Equal[me: Self](other: Self) -> Bool { + return __intrinsic_int_eq(me, other); + } +} + +impl String as EqWith(Self) { + fn Equal[me: Self](other: Self) -> Bool { + return __intrinsic_str_eq(me, other); + } +} + +// ---------------------- +// Arithmetic interfaces. +// ---------------------- + interface Negate { // TODO: = Self let Result:! Type; @@ -99,6 +140,108 @@ external impl i32 as MulWith(i32) where .Result == i32 { external impl i32 as ModWith(i32) where .Result == i32 { fn Op[me: i32](other: i32) -> i32 { return me % other; } } + +// --------------------------------- +// Bitwise and bit-shift interfaces. +// --------------------------------- + +// Unary `^`. +interface BitComplement { + // TODO: = Self + let Result:! Type; + fn Op[me: Self]() -> Result; +} + +// Binary `&`. +interface BitAndWith(U:! Type) { + // TODO: = Self + let Result:! Type; + fn Op[me: Self](other: U) -> Result; +} +// TODO: +// constraint BitAnd { +// extends BitAndWith(Self) where .Result == Self; +// } + +// Binary `|`. +interface BitOrWith(U:! Type) { + // TODO: = Self + let Result:! Type; + fn Op[me: Self](other: U) -> Result; +} +// TODO: +// constraint BitOr { +// extends BitOrWith(Self) where .Result == Self; +// } + +// Binary `^`. +interface BitXorWith(U:! Type) { + // TODO: = Self + let Result:! Type; + fn Op[me: Self](other: U) -> Result; +} +// TODO: +// constraint BitXor { +// extends BitXorWith(Self) where .Result == Self; +// } + +// Binary `<<`. +interface LeftShiftWith(U:! Type) { + // TODO: = Self + let Result:! Type; + fn Op[me: Self](other: U) -> Result; +} +// TODO: +// constraint LeftShift { +// extends LeftShiftWith(Self) where .Result == Self; +// } + +// Binary `>>`. +interface RightShiftWith(U:! Type) { + // TODO: = Self + let Result:! Type; + fn Op[me: Self](other: U) -> Result; +} +// TODO: +// constraint RightShift { +// extends RightShiftWith(Self) where .Result == Self; +// } + +external impl i32 as BitComplement where .Result == i32 { + fn Op[me: i32]() -> i32 { + return __intrinsic_int_bit_complement(me); + } +} +external impl i32 as BitAndWith(i32) where .Result == i32 { + fn Op[me: i32](other: i32) -> i32 { + return __intrinsic_int_bit_and(me, other); + } +} +external impl i32 as BitOrWith(i32) where .Result == i32 { + fn Op[me: i32](other: i32) -> i32 { + return __intrinsic_int_bit_or(me, other); + } +} +external impl i32 as BitXorWith(i32) where .Result == i32 { + fn Op[me: i32](other: i32) -> i32 { + return __intrinsic_int_bit_xor(me, other); + } +} +external impl i32 as LeftShiftWith(i32) where .Result == i32 { + fn Op[me: i32](other: i32) -> i32 { + return __intrinsic_int_left_shift(me, other); + } +} +external impl i32 as RightShiftWith(i32) where .Result == i32 { + fn Op[me: i32](other: i32) -> i32 { + return __intrinsic_int_right_shift(me, other); + } +} + +// ------------------------ +// Miscellaneous utilities. +// ------------------------ + // 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. @@ -120,32 +263,3 @@ class Heap { } var heap: Heap = {}; - -interface EqWith(U:! Type) { - fn Equal[me: Self](other: U) -> Bool; - // TODO: NotEqual with default impl -} -// TODO: constraint Eq { ... } - - -// TODO: Simplify this once we have variadics -impl forall [T2:! Type, U2:! Type, T1:! EqWith(T2), U1:! EqWith(U2)] - (T1, U1) as EqWith((T2, U2)) { - fn Equal[me: Self](other: (T2, U2)) -> Bool { - let (l1: T1, l2: U1) = me; - let (r1: T2, r2: U2) = other; - return l1 == r1 and l2 == r2; - } -} - -impl i32 as EqWith(Self) { - fn Equal[me: Self](other: Self) -> Bool { - return __intrinsic_int_eq(me, other); - } -} - -impl String as EqWith(Self) { - fn Equal[me: Self](other: Self) -> Bool { - return __intrinsic_str_eq(me, other); - } -} diff --git a/explorer/fuzzing/ast_to_proto.cpp b/explorer/fuzzing/ast_to_proto.cpp index 35d52c928a69..a7c4edb8bcc6 100644 --- a/explorer/fuzzing/ast_to_proto.cpp +++ b/explorer/fuzzing/ast_to_proto.cpp @@ -61,8 +61,18 @@ static auto OperatorToProtoEnum(const Operator op) return Fuzzing::OperatorExpression::Or; case Operator::Sub: return Fuzzing::OperatorExpression::Sub; - case Operator::Combine: - return Fuzzing::OperatorExpression::Combine; + case Operator::BitwiseAnd: + return Fuzzing::OperatorExpression::BitwiseAnd; + case Operator::BitwiseOr: + return Fuzzing::OperatorExpression::BitwiseOr; + case Operator::BitwiseXor: + return Fuzzing::OperatorExpression::BitwiseXor; + case Operator::BitShiftLeft: + return Fuzzing::OperatorExpression::BitShiftLeft; + case Operator::BitShiftRight: + return Fuzzing::OperatorExpression::BitShiftRight; + case Operator::Complement: + return Fuzzing::OperatorExpression::Complement; } } @@ -228,29 +238,10 @@ static auto ExpressionToProto(const Expression& expression) case ExpressionKind::IntrinsicExpression: { const auto& intrinsic = cast(expression); - auto* intrinsic_proto = expression_proto.mutable_intrinsic(); - switch (intrinsic.intrinsic()) { - case IntrinsicExpression::Intrinsic::Print: - intrinsic_proto->set_intrinsic(Fuzzing::IntrinsicExpression::Print); - break; - case IntrinsicExpression::Intrinsic::Alloc: - intrinsic_proto->set_intrinsic(Fuzzing::IntrinsicExpression::Alloc); - break; - case IntrinsicExpression::Intrinsic::Dealloc: - intrinsic_proto->set_intrinsic(Fuzzing::IntrinsicExpression::Dealloc); - break; - case IntrinsicExpression::Intrinsic::Rand: - intrinsic_proto->set_intrinsic(Fuzzing::IntrinsicExpression::Rand); - break; - case IntrinsicExpression::Intrinsic::IntEq: - intrinsic_proto->set_intrinsic(Fuzzing::IntrinsicExpression::IntEq); - break; - case IntrinsicExpression::Intrinsic::StrEq: - intrinsic_proto->set_intrinsic(Fuzzing::IntrinsicExpression::StrEq); - break; - } - *intrinsic_proto->mutable_argument() = - TupleLiteralExpressionToProto(intrinsic.args()); + auto* call_proto = expression_proto.mutable_call(); + call_proto->mutable_function()->mutable_identifier()->set_name( + std::string(intrinsic.name())); + *call_proto->mutable_argument() = ExpressionToProto(intrinsic.args()); break; } diff --git a/explorer/interpreter/builtins.h b/explorer/interpreter/builtins.h index 9444c5b2e256..ca43b3f70074 100644 --- a/explorer/interpreter/builtins.h +++ b/explorer/interpreter/builtins.h @@ -27,6 +27,9 @@ class Builtins { As, ImplicitAs, + // Comparison. + EqWith, + // Arithmetic. Negate, AddWith, @@ -34,20 +37,31 @@ class Builtins { MulWith, ModWith, - // Comparison. - EqWith, + // Bitwise and shift. + BitComplement, + BitAndWith, + BitOrWith, + BitXorWith, + LeftShiftWith, + RightShiftWith, - Last = EqWith + Last = RightShiftWith }; // TODO: In C++20, replace with `using enum Builtin;`. static constexpr Builtin As = Builtin::As; static constexpr Builtin ImplicitAs = Builtin::ImplicitAs; + static constexpr Builtin EqWith = Builtin::EqWith; static constexpr Builtin Negate = Builtin::Negate; static constexpr Builtin AddWith = Builtin::AddWith; static constexpr Builtin SubWith = Builtin::SubWith; static constexpr Builtin MulWith = Builtin::MulWith; static constexpr Builtin ModWith = Builtin::ModWith; - static constexpr Builtin EqWith = Builtin::EqWith; + static constexpr Builtin BitComplement = Builtin::BitComplement; + static constexpr Builtin BitAndWith = Builtin::BitAndWith; + static constexpr Builtin BitOrWith = Builtin::BitOrWith; + static constexpr Builtin BitXorWith = Builtin::BitXorWith; + static constexpr Builtin LeftShiftWith = Builtin::LeftShiftWith; + static constexpr Builtin RightShiftWith = Builtin::RightShiftWith; // Register a declaration that might be a builtin. void Register(Nonnull decl); @@ -63,9 +77,10 @@ class Builtins { private: static constexpr int NumBuiltins = static_cast(Builtin::Last) + 1; - static constexpr std::array BuiltinNames = { - "As", "ImplicitAs", "Negate", "AddWith", - "SubWith", "MulWith", "ModWith", "EqWith"}; + static constexpr const char* BuiltinNames[NumBuiltins] = { + "As", "ImplicitAs", "EqWith", "Negate", "AddWith", + "SubWith", "MulWith", "ModWith", "BitComplement", "BitAndWith", + "BitOrWith", "BitXorWith", "LeftShiftWith", "RightShiftWith"}; std::optional> builtins_[NumBuiltins] = {}; }; diff --git a/explorer/interpreter/interpreter.cpp b/explorer/interpreter/interpreter.cpp index 4a1a6deb9d74..494267ea3be5 100644 --- a/explorer/interpreter/interpreter.cpp +++ b/explorer/interpreter/interpreter.cpp @@ -209,12 +209,18 @@ auto Interpreter::EvalPrim(Operator op, Nonnull static_type, return heap_.Read(cast(*args[0]).address(), source_loc); case Operator::AddressOf: return arena_->New(cast(*args[0]).address()); - case Operator::Combine: + case Operator::BitwiseAnd: + // If & wasn't rewritten, it's being used to form a constraint. return &cast(static_type)->constraint_type(); case Operator::As: case Operator::Eq: - CARBON_FATAL() << "These operators should have been rewritten to " - "interface method calls"; + case Operator::BitwiseOr: + case Operator::BitwiseXor: + case Operator::BitShiftLeft: + case Operator::BitShiftRight: + case Operator::Complement: + CARBON_FATAL() << "operator " << ToString(op) + << " should always be rewritten"; } } @@ -1166,19 +1172,9 @@ auto Interpreter::StepExp() -> ErrorOr { std::make_unique(&intrinsic.args())); } // { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H} + const auto& args = cast(*act.results()[0]).elements(); switch (cast(exp).intrinsic()) { - case IntrinsicExpression::Intrinsic::Rand: { - const auto& args = cast(*act.results()[0]).elements(); - CARBON_CHECK(args.size() == 2); - - const auto& low = cast(*args[0]).value(); - const auto& high = cast(*args[1]).value(); - std::uniform_int_distribution<> distr(low, high); - int r = distr(generator); - return todo_.FinishAction(arena_->New(r)); - } case IntrinsicExpression::Intrinsic::Print: { - const auto& args = cast(*act.results()[0]).elements(); CARBON_ASSIGN_OR_RETURN( Nonnull format_string_value, Convert(args[0], arena_->New(), exp.source_loc())); @@ -1200,19 +1196,24 @@ auto Interpreter::StepExp() -> ErrorOr { return todo_.FinishAction(TupleValue::Empty()); } case IntrinsicExpression::Intrinsic::Alloc: { - const auto& args = cast(*act.results()[0]); - CARBON_CHECK(args.elements().size() == 1); - Address addr(heap_.AllocateValue(args.elements()[0])); + CARBON_CHECK(args.size() == 1); + Address addr(heap_.AllocateValue(args[0])); return todo_.FinishAction(arena_->New(addr)); } case IntrinsicExpression::Intrinsic::Dealloc: { - const auto& args = cast(*act.results()[0]); - CARBON_CHECK(args.elements().size() == 1); - heap_.Deallocate(cast(args.elements()[0])->address()); + CARBON_CHECK(args.size() == 1); + heap_.Deallocate(cast(args[0])->address()); return todo_.FinishAction(TupleValue::Empty()); } + case IntrinsicExpression::Intrinsic::Rand: { + CARBON_CHECK(args.size() == 2); + const auto& low = cast(*args[0]).value(); + const auto& high = cast(*args[1]).value(); + std::uniform_int_distribution<> distr(low, high); + int r = distr(generator); + return todo_.FinishAction(arena_->New(r)); + } case IntrinsicExpression::Intrinsic::IntEq: { - const auto& args = cast(*act.results()[0]).elements(); CARBON_CHECK(args.size() == 2); auto lhs = cast(*args[0]).value(); auto rhs = cast(*args[1]).value(); @@ -1220,13 +1221,49 @@ auto Interpreter::StepExp() -> ErrorOr { return todo_.FinishAction(result); } case IntrinsicExpression::Intrinsic::StrEq: { - const auto& args = cast(*act.results()[0]).elements(); CARBON_CHECK(args.size() == 2); auto& lhs = cast(*args[0]).value(); auto& rhs = cast(*args[1]).value(); auto result = arena_->New(lhs == rhs); return todo_.FinishAction(result); } + case IntrinsicExpression::Intrinsic::IntBitComplement: { + CARBON_CHECK(args.size() == 1); + return todo_.FinishAction( + arena_->New(~cast(*args[0]).value())); + } + case IntrinsicExpression::Intrinsic::IntBitAnd: { + CARBON_CHECK(args.size() == 2); + return todo_.FinishAction( + arena_->New(cast(*args[0]).value() & + cast(*args[1]).value())); + } + case IntrinsicExpression::Intrinsic::IntBitOr: { + CARBON_CHECK(args.size() == 2); + return todo_.FinishAction( + arena_->New(cast(*args[0]).value() | + cast(*args[1]).value())); + } + case IntrinsicExpression::Intrinsic::IntBitXor: { + CARBON_CHECK(args.size() == 2); + return todo_.FinishAction( + arena_->New(cast(*args[0]).value() ^ + cast(*args[1]).value())); + } + case IntrinsicExpression::Intrinsic::IntLeftShift: { + CARBON_CHECK(args.size() == 2); + // TODO: Runtime error if RHS is too large. + return todo_.FinishAction(arena_->New( + static_cast(cast(*args[0]).value()) + << cast(*args[1]).value())); + } + case IntrinsicExpression::Intrinsic::IntRightShift: { + CARBON_CHECK(args.size() == 2); + // TODO: Runtime error if RHS is too large. + return todo_.FinishAction( + arena_->New(cast(*args[0]).value() >> + cast(*args[1]).value())); + } } } case ExpressionKind::IntTypeLiteral: { diff --git a/explorer/interpreter/type_checker.cpp b/explorer/interpreter/type_checker.cpp index a577d04b77c2..ff81b1d0c666 100644 --- a/explorer/interpreter/type_checker.cpp +++ b/explorer/interpreter/type_checker.cpp @@ -1944,17 +1944,23 @@ auto TypeChecker::TypeCheckExp(Nonnull e, ts.push_back(&argument->static_type()); } - auto handle_binary_arithmetic = + auto handle_unary_operator = [&](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(); + ErrorOr> result = BuildBuiltinMethodCall( + impl_scope, op.arguments()[0], BuiltinInterfaceName{builtin}, + 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(); + }; - // Now try an overloaded operator. + auto handle_binary_operator = + [&](Builtins::Builtin builtin) -> ErrorOr { ErrorOr> result = BuildBuiltinMethodCall( impl_scope, op.arguments()[0], BuiltinInterfaceName{builtin, ts[1]}, BuiltinMethodCall{"Op", {op.arguments()[1]}}); @@ -1968,26 +1974,32 @@ auto TypeChecker::TypeCheckExp(Nonnull e, return Success(); }; + auto handle_binary_arithmetic = + [&](Builtins::Builtin builtin) -> ErrorOr { + // Handle a built-in operator first. + // TODO: Replace this with an intrinsic. + 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. + return handle_binary_operator(builtin); + }; + switch (op.op()) { case Operator::Neg: { // Handle a built-in negation first. + // TODO: Replace this with an intrinsic. 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(); + return handle_unary_operator(Builtins::Negate); } case Operator::Add: return handle_binary_arithmetic(Builtins::AddWith); @@ -1997,6 +2009,42 @@ auto TypeChecker::TypeCheckExp(Nonnull e, return handle_binary_arithmetic(Builtins::MulWith); case Operator::Mod: return handle_binary_arithmetic(Builtins::ModWith); + case Operator::BitwiseAnd: + // `&` between type-of-types performs constraint combination. + // TODO: Should this be done via an intrinsic? + if (IsTypeOfType(ts[0]) && IsTypeOfType(ts[1])) { + std::optional> constraints[2]; + for (int i : {0, 1}) { + if (auto* iface_type_type = + dyn_cast(ts[i])) { + constraints[i] = MakeConstraintForInterface( + e->source_loc(), &iface_type_type->interface_type()); + } else if (auto* constraint_type_type = + dyn_cast(ts[i])) { + constraints[i] = &constraint_type_type->constraint_type(); + } else { + return CompilationError(op.arguments()[i]->source_loc()) + << "argument to " << ToString(op.op()) + << " should be a constraint, found `" << *ts[i] << "`"; + } + } + op.set_static_type( + arena_->New(CombineConstraints( + e->source_loc(), {*constraints[0], *constraints[1]}))); + op.set_value_category(ValueCategory::Let); + return Success(); + } + return handle_binary_operator(Builtins::BitAndWith); + case Operator::BitwiseOr: + return handle_binary_operator(Builtins::BitOrWith); + case Operator::BitwiseXor: + return handle_binary_operator(Builtins::BitXorWith); + case Operator::BitShiftLeft: + return handle_binary_operator(Builtins::LeftShiftWith); + case Operator::BitShiftRight: + return handle_binary_operator(Builtins::RightShiftWith); + case Operator::Complement: + return handle_unary_operator(Builtins::BitComplement); case Operator::And: CARBON_RETURN_IF_ERROR(ExpectExactType(e->source_loc(), "&&(1)", arena_->New(), ts[0], @@ -2060,27 +2108,6 @@ auto TypeChecker::TypeCheckExp(Nonnull e, op.set_static_type(arena_->New(ts[0])); op.set_value_category(ValueCategory::Let); return Success(); - case Operator::Combine: { - std::optional> constraints[2]; - for (int i : {0, 1}) { - if (auto* iface_type_type = dyn_cast(ts[i])) { - constraints[i] = MakeConstraintForInterface( - e->source_loc(), &iface_type_type->interface_type()); - } else if (auto* constraint_type_type = - dyn_cast(ts[i])) { - constraints[i] = &constraint_type_type->constraint_type(); - } else { - return CompilationError(op.arguments()[i]->source_loc()) - << "argument to " << ToString(op.op()) - << " should be a constraint, found `" << *ts[i] << "`"; - } - } - op.set_static_type( - arena_->New(CombineConstraints( - e->source_loc(), {*constraints[0], *constraints[1]}))); - op.set_value_category(ValueCategory::Let); - return Success(); - } case Operator::As: { CARBON_ASSIGN_OR_RETURN( Nonnull type, @@ -2211,23 +2238,6 @@ auto TypeChecker::TypeCheckExp(Nonnull e, CARBON_RETURN_IF_ERROR(TypeCheckExp(&intrinsic_exp.args(), impl_scope)); const auto& args = intrinsic_exp.args().fields(); switch (cast(*e).intrinsic()) { - case IntrinsicExpression::Intrinsic::Rand: { - if (args.size() != 2) { - return CompilationError(e->source_loc()) - << "Rand takes 2 arguments, received " << args.size(); - } - CARBON_RETURN_IF_ERROR(ExpectExactType( - e->source_loc(), "Rand argument 0", arena_->New(), - &args[0]->static_type(), impl_scope)); - - CARBON_RETURN_IF_ERROR(ExpectExactType( - e->source_loc(), "Rand argument 1", arena_->New(), - &args[1]->static_type(), impl_scope)); - - e->set_static_type(arena_->New()); - - return Success(); - } case IntrinsicExpression::Intrinsic::Print: // TODO: Remove Print special casing once we have variadics or // overloads. Here, that's the name Print instead of __intrinsic_print @@ -2269,6 +2279,23 @@ auto TypeChecker::TypeCheckExp(Nonnull e, e->set_value_category(ValueCategory::Let); return Success(); } + case IntrinsicExpression::Intrinsic::Rand: { + if (args.size() != 2) { + return CompilationError(e->source_loc()) + << "Rand takes 2 arguments, received " << args.size(); + } + CARBON_RETURN_IF_ERROR(ExpectExactType( + e->source_loc(), "Rand argument 0", arena_->New(), + &args[0]->static_type(), impl_scope)); + + CARBON_RETURN_IF_ERROR(ExpectExactType( + e->source_loc(), "Rand argument 1", arena_->New(), + &args[1]->static_type(), impl_scope)); + + e->set_static_type(arena_->New()); + + return Success(); + } case IntrinsicExpression::Intrinsic::IntEq: { if (args.size() != 2) { return CompilationError(e->source_loc()) @@ -2299,6 +2326,35 @@ auto TypeChecker::TypeCheckExp(Nonnull e, e->set_value_category(ValueCategory::Let); return Success(); } + case IntrinsicExpression::Intrinsic::IntBitComplement: + if (args.size() != 1) { + return CompilationError(e->source_loc()) + << intrinsic_exp.name() << " takes 1 argument"; + } + CARBON_RETURN_IF_ERROR(ExpectExactType( + e->source_loc(), "complement argument", arena_->New(), + &args[0]->static_type(), impl_scope)); + e->set_static_type(arena_->New()); + e->set_value_category(ValueCategory::Let); + return Success(); + case IntrinsicExpression::Intrinsic::IntBitAnd: + case IntrinsicExpression::Intrinsic::IntBitOr: + case IntrinsicExpression::Intrinsic::IntBitXor: + case IntrinsicExpression::Intrinsic::IntLeftShift: + case IntrinsicExpression::Intrinsic::IntRightShift: + if (args.size() != 2) { + return CompilationError(e->source_loc()) + << intrinsic_exp.name() << " takes 2 arguments"; + } + CARBON_RETURN_IF_ERROR(ExpectExactType( + e->source_loc(), "argument 1", arena_->New(), + &args[0]->static_type(), impl_scope)); + CARBON_RETURN_IF_ERROR(ExpectExactType( + e->source_loc(), "argument 2", arena_->New(), + &args[1]->static_type(), impl_scope)); + e->set_static_type(arena_->New()); + e->set_value_category(ValueCategory::Let); + return Success(); } } case ExpressionKind::IntTypeLiteral: diff --git a/explorer/syntax/lexer.lpp b/explorer/syntax/lexer.lpp index cda7acbeb563..9aada574894f 100644 --- a/explorer/syntax/lexer.lpp +++ b/explorer/syntax/lexer.lpp @@ -42,6 +42,7 @@ AUTO "auto" AWAIT "__await" BOOL "Bool" BREAK "break" +CARET "^" CASE "case" CHOICE "choice" CLASS "class" @@ -61,6 +62,7 @@ FALSE "false" FN "fn" FN_TYPE "__Fn" FORALL "forall" +GREATER_GREATER ">>" IF "if" IMPL "impl" IMPORT "import" @@ -69,15 +71,17 @@ IS "is" LEFT_CURLY_BRACE "{" LEFT_PARENTHESIS "(" LEFT_SQUARE_BRACKET "[" +LESS_LESS "<<" LET "let" LIBRARY "library" MATCH "match" MINUS "-" -MODULO "%" NOT "not" OR "or" PACKAGE "package" +PERCENT "%" PERIOD "." +PIPE "|" PLUS "+" RETURN "return" RETURNED "returned" @@ -131,6 +135,7 @@ operand_start [(A-Za-z0-9_\"] {AWAIT} { return CARBON_SIMPLE_TOKEN(AWAIT); } {BOOL} { return CARBON_SIMPLE_TOKEN(BOOL); } {BREAK} { return CARBON_SIMPLE_TOKEN(BREAK); } +{CARET} { return CARBON_SIMPLE_TOKEN(CARET); } {CASE} { return CARBON_SIMPLE_TOKEN(CASE); } {CHOICE} { return CARBON_SIMPLE_TOKEN(CHOICE); } {CLASS} { return CARBON_SIMPLE_TOKEN(CLASS); } @@ -150,6 +155,7 @@ operand_start [(A-Za-z0-9_\"] {FN_TYPE} { return CARBON_SIMPLE_TOKEN(FN_TYPE); } {FN} { return CARBON_SIMPLE_TOKEN(FN); } {FORALL} { return CARBON_SIMPLE_TOKEN(FORALL); } +{GREATER_GREATER} { return CARBON_SIMPLE_TOKEN(GREATER_GREATER); } {IF} { return CARBON_SIMPLE_TOKEN(IF); } {IMPL} { return CARBON_SIMPLE_TOKEN(IMPL); } {IMPORT} { return CARBON_SIMPLE_TOKEN(IMPORT); } @@ -158,15 +164,17 @@ operand_start [(A-Za-z0-9_\"] {LEFT_CURLY_BRACE} { return CARBON_SIMPLE_TOKEN(LEFT_CURLY_BRACE); } {LEFT_PARENTHESIS} { return CARBON_SIMPLE_TOKEN(LEFT_PARENTHESIS); } {LEFT_SQUARE_BRACKET} { return CARBON_SIMPLE_TOKEN(LEFT_SQUARE_BRACKET); } +{LESS_LESS} { return CARBON_SIMPLE_TOKEN(LESS_LESS); } {LET} { return CARBON_SIMPLE_TOKEN(LET); } {LIBRARY} { return CARBON_SIMPLE_TOKEN(LIBRARY); } {MATCH} { return CARBON_SIMPLE_TOKEN(MATCH); } {MINUS} { return CARBON_SIMPLE_TOKEN(MINUS); } -{MODULO} { return CARBON_SIMPLE_TOKEN(MODULO); } {NOT} { return CARBON_SIMPLE_TOKEN(NOT); } {OR} { return CARBON_SIMPLE_TOKEN(OR); } {PACKAGE} { return CARBON_SIMPLE_TOKEN(PACKAGE); } +{PERCENT} { return CARBON_SIMPLE_TOKEN(PERCENT); } {PERIOD} { return CARBON_SIMPLE_TOKEN(PERIOD); } +{PIPE} { return CARBON_SIMPLE_TOKEN(PIPE); } {PLUS} { return CARBON_SIMPLE_TOKEN(PLUS); } {RETURNED} { return CARBON_SIMPLE_TOKEN(RETURNED); } {RETURN} { return CARBON_SIMPLE_TOKEN(RETURN); } diff --git a/explorer/syntax/parser.ypp b/explorer/syntax/parser.ypp index 62f867ebaa29..b8df7c3bc277 100644 --- a/explorer/syntax/parser.ypp +++ b/explorer/syntax/parser.ypp @@ -119,20 +119,26 @@ %type > primary_expression %type > postfix_expression %type > ref_deref_expression -%type > combine_lhs -%type > combine_expression %type > type_expression %type > fn_type_expression %type > minus_expression -%type > multiplicative_operand +%type > complement_expression +%type > unary_expression +%type > simple_binary_operand %type > multiplicative_lhs %type > multiplicative_expression %type > additive_operand %type > additive_lhs %type > additive_expression -%type > modulo_operand %type > modulo_expression -%type > as_operand +%type > bitwise_and_lhs +%type > bitwise_and_expression +%type > bitwise_or_lhs +%type > bitwise_or_expression +%type > bitwise_xor_lhs +%type > bitwise_xor_expression +%type > bitwise_expression +%type > bit_shift_expression %type > as_expression %type > unimpl_expression %type > value_expression @@ -197,6 +203,7 @@ AWAIT BOOL BREAK + CARET CASE CHOICE CLASS @@ -216,6 +223,7 @@ FN FN_TYPE FORALL + GREATER_GREATER IF IMPL IMPORT @@ -224,15 +232,17 @@ LEFT_CURLY_BRACE LEFT_PARENTHESIS LEFT_SQUARE_BRACKET + LESS_LESS LET LIBRARY MATCH MINUS - MODULO NOT OR PACKAGE + PERCENT PERIOD + PIPE PLUS RETURN RETURNED @@ -406,21 +416,9 @@ fn_type_expression: FN_TYPE tuple ARROW type_expression { $$ = arena->New(context.source_loc(), $2, $4); } ; -combine_lhs: - ref_deref_expression -| combine_expression -; -combine_expression: - combine_lhs AMPERSAND ref_deref_expression - { - $$ = arena->New( - context.source_loc(), Operator::Combine, - std::vector>({$1, $3})); - } -; type_expression: ref_deref_expression -| combine_expression +| bitwise_and_expression | fn_type_expression ; minus_expression: @@ -432,41 +430,44 @@ minus_expression: std::vector>({$2})); } ; -multiplicative_operand: +complement_expression: + // ref_deref_expression excluded due to precedence diamond. + CARET ref_deref_expression + { + $$ = arena->New( + context.source_loc(), Operator::Complement, + std::vector>({$2})); + } +; +unary_expression: + // ref_deref_expression excluded due to precedence diamond. + minus_expression +| complement_expression +; +// A simple_binary_operand is an operand of a binary operator +// that is not itself a binary operator expression. +simple_binary_operand: ref_deref_expression -| minus_expression +| unary_expression ; multiplicative_lhs: - ref_deref_expression + simple_binary_operand | multiplicative_expression ; multiplicative_expression: - minus_expression -| multiplicative_lhs BINARY_STAR multiplicative_operand + multiplicative_lhs BINARY_STAR simple_binary_operand { $$ = arena->New( context.source_loc(), Operator::Mul, std::vector>({$1, $3})); } ; -modulo_operand: - ref_deref_expression -| minus_expression -; -modulo_expression: - modulo_operand MODULO modulo_operand - { - $$ = arena->New( - context.source_loc(), Operator::Mod, - std::vector>({$1, $3})); - } -; additive_operand: - ref_deref_expression + simple_binary_operand | multiplicative_expression ; additive_lhs: - ref_deref_expression + simple_binary_operand | additive_expression ; additive_expression: @@ -484,12 +485,71 @@ additive_expression: std::vector>({$1, $3})); } ; -as_operand: - minus_expression -| ref_deref_expression +modulo_expression: + simple_binary_operand PERCENT simple_binary_operand + { + $$ = arena->New( + context.source_loc(), Operator::Mod, + std::vector>({$1, $3})); + } +; +bitwise_and_lhs: + simple_binary_operand +| bitwise_and_expression +; +bitwise_and_expression: + bitwise_and_lhs AMPERSAND simple_binary_operand + { + $$ = arena->New( + context.source_loc(), Operator::BitwiseAnd, + std::vector>({$1, $3})); + } +; +bitwise_or_lhs: + simple_binary_operand +| bitwise_or_expression +; +bitwise_or_expression: + bitwise_or_lhs PIPE simple_binary_operand + { + $$ = arena->New( + context.source_loc(), Operator::BitwiseOr, + std::vector>({$1, $3})); + } +; +bitwise_xor_lhs: + simple_binary_operand +| bitwise_xor_expression +; +bitwise_xor_expression: + bitwise_xor_lhs CARET simple_binary_operand + { + $$ = arena->New( + context.source_loc(), Operator::BitwiseXor, + std::vector>({$1, $3})); + } +; +bitwise_expression: + bitwise_and_expression +| bitwise_or_expression +| bitwise_xor_expression +; +bit_shift_expression: + simple_binary_operand LESS_LESS simple_binary_operand + { + $$ = arena->New( + context.source_loc(), Operator::BitShiftLeft, + std::vector>({$1, $3})); + } +| simple_binary_operand GREATER_GREATER simple_binary_operand + { + $$ = arena->New( + context.source_loc(), Operator::BitShiftRight, + std::vector>({$1, $3})); + } ; as_expression: - as_operand AS as_operand + simple_binary_operand AS simple_binary_operand { $$ = arena->New( context.source_loc(), Operator::As, @@ -507,10 +567,12 @@ unimpl_expression: value_expression: // ref_deref_expression excluded due to precedence diamond. additive_expression -| modulo_expression | as_expression -| combine_expression +| bitwise_expression +| bit_shift_expression | fn_type_expression +| modulo_expression +| unary_expression | unimpl_expression ; comparison_operand: diff --git a/explorer/testdata/basic_syntax/fail_missing_var.carbon b/explorer/testdata/basic_syntax/fail_missing_var.carbon index 7b209be68ba7..26cc5a536f1e 100644 --- a/explorer/testdata/basic_syntax/fail_missing_var.carbon +++ b/explorer/testdata/basic_syntax/fail_missing_var.carbon @@ -12,7 +12,7 @@ package ExplorerTest api; fn Main() -> i32 { // error - // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_missing_var.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting EQUAL or SEMICOLON + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_missing_var.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting MINUS or PLUS x : i32; return 1; } diff --git a/explorer/testdata/basic_syntax/fail_var_named_self.carbon b/explorer/testdata/basic_syntax/fail_var_named_self.carbon index 79e9e2ded9e7..fa74e15b4bc6 100644 --- a/explorer/testdata/basic_syntax/fail_var_named_self.carbon +++ b/explorer/testdata/basic_syntax/fail_var_named_self.carbon @@ -14,7 +14,7 @@ fn Main() -> i32 { // Error: can't use keyword `Self` as the name of a variable. // TODO: Current error message is unclear, better would be to say // something like: unexpected `Self`, expecting identifier - // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_var_named_self.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting EQUAL or SEMICOLON + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_var_named_self.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting MINUS or PLUS var Self : i32 = 0; return Self; } diff --git a/explorer/testdata/let/fail_local_named_self.carbon b/explorer/testdata/let/fail_local_named_self.carbon index 495f82098741..41322c04db97 100644 --- a/explorer/testdata/let/fail_local_named_self.carbon +++ b/explorer/testdata/let/fail_local_named_self.carbon @@ -12,7 +12,7 @@ package ExplorerTest api; fn Main() -> i32 { // Error: Can't use keyword `Self` as the name of a local. - // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/let/fail_local_named_self.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting EQUAL + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/let/fail_local_named_self.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting MINUS or PLUS let Self: auto = 10; return 0; } diff --git a/explorer/testdata/operators/bit_and.carbon b/explorer/testdata/operators/bit_and.carbon new file mode 100644 index 000000000000..08f633ad8576 --- /dev/null +++ b/explorer/testdata/operators/bit_and.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: 1 + +package ExplorerTest api; + +class A { var n: i32; } + +external impl A as BitAndWith(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/bit_complement.carbon b/explorer/testdata/operators/bit_complement.carbon new file mode 100644 index 000000000000..ee13e9ac3ca1 --- /dev/null +++ b/explorer/testdata/operators/bit_complement.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 BitComplement 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/bit_or.carbon b/explorer/testdata/operators/bit_or.carbon new file mode 100644 index 000000000000..3487110474bc --- /dev/null +++ b/explorer/testdata/operators/bit_or.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 BitOrWith(i32) where .Result == A { + fn Op[me: Self](rhs: i32) -> A { return {.n = me.n | rhs}; } +} + +fn Main() -> i32 { + var a: A = {.n = 4}; + a = a | 1; + return a.n; +} diff --git a/explorer/testdata/operators/bit_xor.carbon b/explorer/testdata/operators/bit_xor.carbon new file mode 100644 index 000000000000..d9255cb39e18 --- /dev/null +++ b/explorer/testdata/operators/bit_xor.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 BitXorWith(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/bitwise.carbon b/explorer/testdata/operators/bitwise.carbon new file mode 100644 index 000000000000..ec82042d997c --- /dev/null +++ b/explorer/testdata/operators/bitwise.carbon @@ -0,0 +1,32 @@ +// 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: 0 + +package ExplorerTest api; + +fn Main() -> i32 { + if (not (^0 == -1)) { return 11; } + if (not (^1 == -2)) { return 12; } + if (not (^(-3) == 2)) { return 13; } + + if (not (3 & 6 == 2)) { return 21; } + if (not (-1 & 4 == 4)) { return 22; } + if (not (-1 & -2 == -2)) { return 23; } + + if (not (1 | 4 == 5)) { return 31; } + if (not (5 | 3 == 7)) { return 32; } + if (not (-2 | 1 == -1)) { return 33; } + + if (not (1 ^ 4 == 5)) { return 41; } + if (not (5 ^ 3 == 6)) { return 42; } + if (not (-2 ^ -3 == 3)) { return 43; } + + return 0; +} diff --git a/explorer/testdata/operators/left_shift.carbon b/explorer/testdata/operators/left_shift.carbon new file mode 100644 index 000000000000..02ec75e691ea --- /dev/null +++ b/explorer/testdata/operators/left_shift.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 LeftShiftWith(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/right_shift.carbon b/explorer/testdata/operators/right_shift.carbon new file mode 100644 index 000000000000..c539dedac4d6 --- /dev/null +++ b/explorer/testdata/operators/right_shift.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: 2 + +package ExplorerTest api; + +class A { var n: i32; } + +external impl A as RightShiftWith(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/shift.carbon b/explorer/testdata/operators/shift.carbon new file mode 100644 index 000000000000..920e706887ab --- /dev/null +++ b/explorer/testdata/operators/shift.carbon @@ -0,0 +1,27 @@ +// 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: 0 + +package ExplorerTest api; + +fn Main() -> i32 { + if (not (1 << 0 == 1)) { return 1; } + if (not (1 << 3 == 8)) { return 2; } + if (not (0 << 3 == 0)) { return 3; } + if (not (3 << 1 == 6)) { return 4; } + if (not (-1 << 2 == -4)) { return 5; } + if (not (1 >> 0 == 1)) { return 6; } + if (not (1 >> 1 == 0)) { return 7; } + if (not (3 >> 1 == 1)) { return 8; } + if (not (-1 >> 1 == -1)) { return 9; } + if (not (-2 >> 1 == -1)) { return 10; } + + return 0; +}