Add support for compound assignment and increment (#2526)

Add support for user-defined assignment, as well as compound assignment and increment, following the design direction in pending proposal #2511.

Some of this isn't fully testable yet: because explorer doesn't properly support `impl` specialization, the blanket `impl`s in the prelude prevent types from customizing assignment.
This commit is contained in:
Richard Smith
2023-01-20 18:48:13 -08:00
committed by GitHub
parent f1a0645551
commit 2fd7e2b65d
32 changed files with 975 additions and 215 deletions
+20
View File
@@ -241,8 +241,27 @@ message ExpressionStatement {
}
message AssignStatement {
enum Operator {
Plain = 0;
Add = 1;
And = 2;
Div = 3;
Mul = 4;
Or = 5;
Sub = 6;
Mod = 7;
Xor = 8;
ShiftLeft = 9;
ShiftRight = 10;
}
optional Expression lhs = 1;
optional Expression rhs = 2;
optional Operator op = 3;
}
message IncrementDecrementStatement {
optional Expression operand = 1;
optional bool is_increment = 2;
}
message VariableDefinitionStatement {
@@ -321,6 +340,7 @@ message Statement {
BreakStatement break_statement = 13;
ContinueStatement continue_statement = 14;
ForStatement for_statement = 15;
IncrementDecrementStatement inc_dec = 16;
}
}
+43 -1
View File
@@ -534,12 +534,54 @@ static auto StatementToCarbon(const Fuzzing::Statement& statement,
case Fuzzing::Statement::kAssign: {
const auto& assign_statement = statement.assign();
ExpressionToCarbon(assign_statement.lhs(), out);
out << " = ";
switch (assign_statement.op()) {
case Fuzzing::AssignStatement::Plain:
out << " = ";
break;
case Fuzzing::AssignStatement::Add:
out << " += ";
break;
case Fuzzing::AssignStatement::And:
out << " &= ";
break;
case Fuzzing::AssignStatement::Div:
out << " /= ";
break;
case Fuzzing::AssignStatement::Mod:
out << " %= ";
break;
case Fuzzing::AssignStatement::Mul:
out << " *= ";
break;
case Fuzzing::AssignStatement::Or:
out << " |= ";
break;
case Fuzzing::AssignStatement::ShiftLeft:
out << " <<= ";
break;
case Fuzzing::AssignStatement::ShiftRight:
out << " >>= ";
break;
case Fuzzing::AssignStatement::Sub:
out << " -= ";
break;
case Fuzzing::AssignStatement::Xor:
out << " ^= ";
break;
}
ExpressionToCarbon(assign_statement.rhs(), out);
out << ";";
break;
}
case Fuzzing::Statement::kIncDec: {
const auto& inc_dec_statement = statement.inc_dec();
out << (inc_dec_statement.is_increment() ? "++" : "--");
ExpressionToCarbon(inc_dec_statement.operand(), out);
out << ";";
break;
}
case Fuzzing::Statement::kVariableDefinition: {
const auto& def = statement.variable_definition();
if (def.is_returned()) {
+1
View File
@@ -36,6 +36,7 @@ class AlternativeSignature : AstNode;
abstract class Statement : AstNode;
class ExpressionStatement : Statement;
class Assign : Statement;
class IncrementDecrement : Statement;
class VariableDefinition : Statement;
class If : Statement;
abstract class Return : Statement;
+34 -1
View File
@@ -72,7 +72,13 @@ void Statement::PrintDepth(int depth, llvm::raw_ostream& out) const {
break;
case StatementKind::Assign: {
const auto& assign = cast<Assign>(*this);
out << assign.lhs() << " = " << assign.rhs() << ";";
out << assign.lhs() << " " << AssignOperatorToString(assign.op()) << " "
<< assign.rhs() << ";";
break;
}
case StatementKind::IncrementDecrement: {
const auto& inc_dec = cast<IncrementDecrement>(*this);
out << (inc_dec.is_increment() ? "++" : "--") << inc_dec.argument();
break;
}
case StatementKind::If: {
@@ -137,4 +143,31 @@ void Statement::PrintDepth(int depth, llvm::raw_ostream& out) const {
}
}
auto AssignOperatorToString(AssignOperator op) -> std::string_view {
switch (op) {
case AssignOperator::Plain:
return "=";
case AssignOperator::Add:
return "+=";
case AssignOperator::Div:
return "/=";
case AssignOperator::Mul:
return "*=";
case AssignOperator::Mod:
return "%=";
case AssignOperator::Sub:
return "-=";
case AssignOperator::And:
return "&=";
case AssignOperator::Or:
return "|=";
case AssignOperator::Xor:
return "^=";
case AssignOperator::ShiftLeft:
return "<<=";
case AssignOperator::ShiftRight:
return ">>=";
}
}
} // namespace Carbon
+76 -2
View File
@@ -86,11 +86,31 @@ class ExpressionStatement : public Statement {
Nonnull<Expression*> expression_;
};
enum class AssignOperator {
Plain,
Add,
Div,
Mul,
Mod,
Sub,
And,
Or,
Xor,
ShiftLeft,
ShiftRight,
};
// Returns the spelling of this assignment operator token.
auto AssignOperatorToString(AssignOperator op) -> std::string_view;
class Assign : public Statement {
public:
Assign(SourceLocation source_loc, Nonnull<Expression*> lhs,
Assign(SourceLocation source_loc, Nonnull<Expression*> lhs, AssignOperator op,
Nonnull<Expression*> rhs)
: Statement(AstNodeKind::Assign, source_loc), lhs_(lhs), rhs_(rhs) {}
: Statement(AstNodeKind::Assign, source_loc),
lhs_(lhs),
rhs_(rhs),
op_(op) {}
static auto classof(const AstNode* node) -> bool {
return InheritsFromAssign(node->kind());
@@ -101,12 +121,66 @@ class Assign : public Statement {
auto rhs() const -> const Expression& { return *rhs_; }
auto rhs() -> Expression& { return *rhs_; }
auto op() const -> AssignOperator { return op_; }
// Can only be called by type-checking, if a conversion was required.
void set_rhs(Nonnull<Expression*> rhs) { rhs_ = rhs; }
// Set the rewritten form of this statement. Can only be called during type
// checking.
auto set_rewritten_form(Nonnull<const Expression*> rewritten_form) -> void {
CARBON_CHECK(!rewritten_form_.has_value()) << "rewritten form set twice";
rewritten_form_ = rewritten_form;
}
// Get the rewritten form of this statement. A rewritten form is used when
// the statement is rewritten as a function call on an interface. A
// rewritten form is not used when providing built-in operator semantics for
// a plain assignment.
auto rewritten_form() const -> std::optional<Nonnull<const Expression*>> {
return rewritten_form_;
}
private:
Nonnull<Expression*> lhs_;
Nonnull<Expression*> rhs_;
AssignOperator op_;
std::optional<Nonnull<const Expression*>> rewritten_form_;
};
class IncrementDecrement : public Statement {
public:
IncrementDecrement(SourceLocation source_loc, Nonnull<Expression*> argument,
bool is_increment)
: Statement(AstNodeKind::IncrementDecrement, source_loc),
argument_(argument),
is_increment_(is_increment) {}
static auto classof(const AstNode* node) -> bool {
return InheritsFromIncrementDecrement(node->kind());
}
auto argument() const -> const Expression& { return *argument_; }
auto argument() -> Expression& { return *argument_; }
bool is_increment() const { return is_increment_; }
// Set the rewritten form of this statement. Can only be called during type
// checking.
auto set_rewritten_form(Nonnull<const Expression*> rewritten_form) -> void {
CARBON_CHECK(!rewritten_form_.has_value()) << "rewritten form set twice";
rewritten_form_ = rewritten_form;
}
// Get the rewritten form of this statement.
auto rewritten_form() const -> std::optional<Nonnull<const Expression*>> {
return rewritten_form_;
}
private:
Nonnull<Expression*> argument_;
bool is_increment_;
std::optional<Nonnull<const Expression*>> rewritten_form_;
};
class VariableDefinition : public Statement {
+162
View File
@@ -501,6 +501,168 @@ external impl i32 as RightShiftWith(i32) where .Result = i32 {
}
}
// -----------------------------------
// Assignment and compound assignment.
// -----------------------------------
interface AssignWith(U:! type) {
fn Op[addr self: Self*](other: U);
}
constraint Assign { extends AssignWith(Self); }
interface AddAssignWith(U:! type) {
fn Op[addr self: Self*](other: U);
}
constraint AddAssign { extends AddAssignWith(Self); }
interface SubAssignWith(U:! type) {
fn Op[addr self: Self*](other: U);
}
constraint SubAssign { extends SubAssignWith(Self); }
interface MulAssignWith(U:! type) {
fn Op[addr self: Self*](other: U);
}
constraint MulAssign { extends MulAssignWith(Self); }
interface DivAssignWith(U:! type) {
fn Op[addr self: Self*](other: U);
}
constraint DivAssign { extends DivAssignWith(Self); }
interface ModAssignWith(U:! type) {
fn Op[addr self: Self*](other: U);
}
constraint ModAssign { extends ModAssignWith(Self); }
interface BitAndAssignWith(U:! type) {
fn Op[addr self: Self*](other: U);
}
constraint BitAssignAnd { extends BitAndAssignWith(Self); }
interface BitOrAssignWith(U:! type) {
fn Op[addr self: Self*](other: U);
}
constraint BitAssignOr { extends BitOrAssignWith(Self); }
interface BitXorAssignWith(U:! type) {
fn Op[addr self: Self*](other: U);
}
constraint BitAssignXor { extends BitXorAssignWith(Self); }
interface LeftShiftAssignWith(U:! type) {
fn Op[addr self: Self*](other: U);
}
constraint LeftShiftAssign { extends LeftShiftAssignWith(Self); }
interface RightShiftAssignWith(U:! type) {
fn Op[addr self: Self*](other: U);
}
constraint RightShiftAssign { extends RightShiftAssignWith(Self); }
// TODO: This is temporary, and should eventually be replaced by
// something more fine-grained. Not all class types should be
// assignable.
impl forall [T:! type, U:! ImplicitAs(T)]
T as AssignWith(U) {
fn Op[addr self: Self*](other: U) {
*self = other.Convert();
}
}
// TODO: Should `AddWith(U) & AssignWith(.Self.(AddWith(U).Result))` work?
impl forall [U:! type, T:! AddWith(U) where .Self is AssignWith(.Self.Result)]
T as AddAssignWith(U) {
fn Op[addr self: Self*](other: U) {
*self = *self + other;
}
}
impl forall [U:! type, T:! SubWith(U) where .Self is AssignWith(.Self.Result)]
T as SubAssignWith(U) {
fn Op[addr self: Self*](other: U) {
*self = *self - other;
}
}
impl forall [U:! type, T:! MulWith(U) where .Self is AssignWith(.Self.Result)]
T as MulAssignWith(U) {
fn Op[addr self: Self*](other: U) {
*self = *self * other;
}
}
impl forall [U:! type, T:! DivWith(U) where .Self is AssignWith(.Self.Result)]
T as DivAssignWith(U) {
fn Op[addr self: Self*](other: U) {
*self = *self / other;
}
}
impl forall [U:! type, T:! ModWith(U) where .Self is AssignWith(.Self.Result)]
T as ModAssignWith(U) {
fn Op[addr self: Self*](other: U) {
*self = *self % other;
}
}
impl forall [U:! type, T:! BitAndWith(U) where .Self is AssignWith(.Self.Result)]
T as BitAndAssignWith(U) {
fn Op[addr self: Self*](other: U) {
*self = *self & other;
}
}
impl forall [U:! type, T:! BitOrWith(U) where .Self is AssignWith(.Self.Result)]
T as BitOrAssignWith(U) {
fn Op[addr self: Self*](other: U) {
*self = *self | other;
}
}
impl forall [U:! type, T:! BitXorWith(U) where .Self is AssignWith(.Self.Result)]
T as BitXorAssignWith(U) {
fn Op[addr self: Self*](other: U) {
*self = *self ^ other;
}
}
impl forall [U:! type, T:! LeftShiftWith(U) where .Self is AssignWith(.Self.Result)]
T as LeftShiftAssignWith(U) {
fn Op[addr self: Self*](other: U) {
*self = *self << other;
}
}
impl forall [U:! type, T:! RightShiftWith(U) where .Self is AssignWith(.Self.Result)]
T as RightShiftAssignWith(U) {
fn Op[addr self: Self*](other: U) {
*self = *self >> other;
}
}
// ------------------------
// Increment and decrement.
// ------------------------
interface Inc {
fn Op[addr self: Self*]();
}
interface Dec {
fn Op[addr self: Self*]();
}
impl i32 as Inc {
fn Op[addr self: Self*]() {
*self = *self + 1;
}
}
impl i32 as Dec {
fn Op[addr self: Self*]() {
*self = *self - 1;
}
}
//-------------------------
// Optional
//-------------------------
+37
View File
@@ -89,6 +89,34 @@ static auto OperatorToProtoEnum(const Operator op)
}
}
static auto AssignOperatorToProtoEnum(const AssignOperator op)
-> Fuzzing::AssignStatement::Operator {
switch (op) {
case AssignOperator::Plain:
return Fuzzing::AssignStatement::Plain;
case AssignOperator::Add:
return Fuzzing::AssignStatement::Add;
case AssignOperator::And:
return Fuzzing::AssignStatement::And;
case AssignOperator::Mul:
return Fuzzing::AssignStatement::Mul;
case AssignOperator::Div:
return Fuzzing::AssignStatement::Div;
case AssignOperator::Mod:
return Fuzzing::AssignStatement::Mod;
case AssignOperator::Or:
return Fuzzing::AssignStatement::Or;
case AssignOperator::ShiftLeft:
return Fuzzing::AssignStatement::ShiftLeft;
case AssignOperator::ShiftRight:
return Fuzzing::AssignStatement::ShiftRight;
case AssignOperator::Sub:
return Fuzzing::AssignStatement::Sub;
case AssignOperator::Xor:
return Fuzzing::AssignStatement::Xor;
}
}
static auto FieldInitializerToProto(const FieldInitializer& field)
-> Fuzzing::FieldInitializer {
Fuzzing::FieldInitializer field_proto;
@@ -435,6 +463,15 @@ static auto StatementToProto(const Statement& statement) -> Fuzzing::Statement {
auto* assign_proto = statement_proto.mutable_assign();
*assign_proto->mutable_lhs() = ExpressionToProto(assign.lhs());
*assign_proto->mutable_rhs() = ExpressionToProto(assign.rhs());
assign_proto->set_op(AssignOperatorToProtoEnum(assign.op()));
break;
}
case StatementKind::IncrementDecrement: {
const auto& inc_dec = cast<IncrementDecrement>(statement);
auto* inc_dec_proto = statement_proto.mutable_inc_dec();
*inc_dec_proto->mutable_operand() = ExpressionToProto(inc_dec.argument());
inc_dec_proto->set_is_increment(inc_dec.is_increment());
break;
}
+67 -7
View File
@@ -51,7 +51,26 @@ class Builtins {
LeftShiftWith,
RightShiftWith,
Last = RightShiftWith
// Simple assignment.
AssignWith,
// Compound assignment.
AddAssignWith,
SubAssignWith,
MulAssignWith,
DivAssignWith,
ModAssignWith,
BitAndAssignWith,
BitOrAssignWith,
BitXorAssignWith,
LeftShiftAssignWith,
RightShiftAssignWith,
// Increment and decrement.
Inc,
Dec,
Last = Dec
};
// TODO: In C++20, replace with `using enum Builtin;`.
static constexpr Builtin As = Builtin::As;
@@ -61,6 +80,7 @@ class Builtins {
static constexpr Builtin LessEqWith = Builtin::LessEqWith;
static constexpr Builtin GreaterWith = Builtin::GreaterWith;
static constexpr Builtin GreaterEqWith = Builtin::GreaterEqWith;
static constexpr Builtin CompareWith = Builtin::CompareWith;
static constexpr Builtin Negate = Builtin::Negate;
static constexpr Builtin AddWith = Builtin::AddWith;
static constexpr Builtin SubWith = Builtin::SubWith;
@@ -73,7 +93,19 @@ class Builtins {
static constexpr Builtin BitXorWith = Builtin::BitXorWith;
static constexpr Builtin LeftShiftWith = Builtin::LeftShiftWith;
static constexpr Builtin RightShiftWith = Builtin::RightShiftWith;
static constexpr Builtin CompareWith = Builtin::CompareWith;
static constexpr Builtin AssignWith = Builtin::AssignWith;
static constexpr Builtin AddAssignWith = Builtin::AddAssignWith;
static constexpr Builtin SubAssignWith = Builtin::SubAssignWith;
static constexpr Builtin MulAssignWith = Builtin::MulAssignWith;
static constexpr Builtin DivAssignWith = Builtin::DivAssignWith;
static constexpr Builtin ModAssignWith = Builtin::ModAssignWith;
static constexpr Builtin BitAndAssignWith = Builtin::BitAndAssignWith;
static constexpr Builtin BitOrAssignWith = Builtin::BitOrAssignWith;
static constexpr Builtin BitXorAssignWith = Builtin::BitXorAssignWith;
static constexpr Builtin LeftShiftAssignWith = Builtin::LeftShiftAssignWith;
static constexpr Builtin RightShiftAssignWith = Builtin::RightShiftAssignWith;
static constexpr Builtin Inc = Builtin::Inc;
static constexpr Builtin Dec = Builtin::Dec;
// Register a declaration that might be a builtin.
void Register(Nonnull<const Declaration*> decl);
@@ -90,11 +122,39 @@ class Builtins {
private:
static constexpr int NumBuiltins = static_cast<int>(Builtin::Last) + 1;
static constexpr const char* BuiltinNames[NumBuiltins] = {
"As", "ImplicitAs", "EqWith", "LessWith",
"LessEqWith", "GreaterWith", "GreaterEqWith", "CompareWith",
"Negate", "AddWith", "SubWith", "MulWith",
"DivWith", "ModWith", "BitComplement", "BitAndWith",
"BitOrWith", "BitXorWith", "LeftShiftWith", "RightShiftWith"};
"As",
"ImplicitAs",
"EqWith",
"LessWith",
"LessEqWith",
"GreaterWith",
"GreaterEqWith",
"CompareWith",
"Negate",
"AddWith",
"SubWith",
"MulWith",
"DivWith",
"ModWith",
"BitComplement",
"BitAndWith",
"BitOrWith",
"BitXorWith",
"LeftShiftWith",
"RightShiftWith",
"AssignWith",
"AddAssignWith",
"SubAssignWith",
"MulAssignWith",
"DivAssignWith",
"ModAssignWith",
"BitAndAssignWith",
"BitOrAssignWith",
"BitXorAssignWith",
"LeftShiftAssignWith",
"RightShiftAssignWith",
"Inc",
"Dec"};
std::optional<Nonnull<const Declaration*>> builtins_[NumBuiltins] = {};
};
+16
View File
@@ -1937,6 +1937,13 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
}
case StatementKind::Assign: {
const auto& assign = cast<Assign>(stmt);
if (auto rewrite = assign.rewritten_form()) {
if (act.pos() == 0) {
return todo_.Spawn(std::make_unique<ExpressionAction>(*rewrite));
} else {
return todo_.FinishAction();
}
}
if (act.pos() == 0) {
// { {(lv = e) :: C, E, F} :: S, H}
// -> { {lv :: ([] = e) :: C, E, F} :: S, H}
@@ -1958,6 +1965,15 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
return todo_.FinishAction();
}
}
case StatementKind::IncrementDecrement: {
const auto& inc_dec = cast<IncrementDecrement>(stmt);
if (act.pos() == 0) {
return todo_.Spawn(
std::make_unique<ExpressionAction>(*inc_dec.rewritten_form()));
} else {
return todo_.FinishAction();
}
}
case StatementKind::If:
if (act.pos() == 0) {
// { {(if (e) then_stmt else else_stmt) :: C, E, F} :: S, H}
@@ -130,6 +130,7 @@ static auto ResolveControlFlow(Nonnull<Statement*> statement,
return Success();
case StatementKind::ExpressionStatement:
case StatementKind::Assign:
case StatementKind::IncrementDecrement:
case StatementKind::VariableDefinition:
case StatementKind::Run:
case StatementKind::Await:
+5
View File
@@ -380,6 +380,11 @@ static auto ResolveNames(Statement& statement, StaticScope& enclosing_scope)
CARBON_RETURN_IF_ERROR(ResolveNames(assign.rhs(), enclosing_scope));
break;
}
case StatementKind::IncrementDecrement: {
auto& inc_dec = cast<IncrementDecrement>(statement);
CARBON_RETURN_IF_ERROR(ResolveNames(inc_dec.argument(), enclosing_scope));
break;
}
case StatementKind::VariableDefinition: {
auto& def = cast<VariableDefinition>(statement);
if (def.has_init()) {
+10 -1
View File
@@ -222,7 +222,10 @@ static auto ResolveUnformed(Nonnull<const Statement*> statement,
}
case StatementKind::Assign: {
const auto& assign = cast<Assign>(*statement);
if (assign.lhs().kind() == ExpressionKind::IdentifierExpression) {
if (assign.op() != AssignOperator::Plain) {
CARBON_RETURN_IF_ERROR(ResolveUnformed(&assign.lhs(), flow_facts,
FlowFacts::ActionType::Check));
} else if (assign.lhs().kind() == ExpressionKind::IdentifierExpression) {
CARBON_RETURN_IF_ERROR(ResolveUnformed(&assign.lhs(), flow_facts,
FlowFacts::ActionType::Form));
} else {
@@ -234,6 +237,12 @@ static auto ResolveUnformed(Nonnull<const Statement*> statement,
FlowFacts::ActionType::Check));
break;
}
case StatementKind::IncrementDecrement: {
CARBON_RETURN_IF_ERROR(
ResolveUnformed(&cast<IncrementDecrement>(statement)->argument(),
flow_facts, FlowFacts::ActionType::Check));
break;
}
case StatementKind::ExpressionStatement: {
const auto& exp_stmt = cast<ExpressionStatement>(*statement);
CARBON_RETURN_IF_ERROR(
+62 -4
View File
@@ -4090,6 +4090,36 @@ auto TypeChecker::TypeCheckGenericBinding(GenericBinding& binding,
return Success();
}
// Get the builtin interface that should be used for the given kind of
// assignment operator.
static Builtins::Builtin GetBuiltinInterfaceForAssignOperator(
AssignOperator op) {
switch (op) {
case AssignOperator::Plain:
return Builtins::AssignWith;
case AssignOperator::Add:
return Builtins::AddAssignWith;
case AssignOperator::Sub:
return Builtins::SubAssignWith;
case AssignOperator::Mul:
return Builtins::MulAssignWith;
case AssignOperator::Div:
return Builtins::DivAssignWith;
case AssignOperator::Mod:
return Builtins::ModAssignWith;
case AssignOperator::And:
return Builtins::BitAndAssignWith;
case AssignOperator::Or:
return Builtins::BitOrAssignWith;
case AssignOperator::Xor:
return Builtins::BitXorAssignWith;
case AssignOperator::ShiftLeft:
return Builtins::LeftShiftAssignWith;
case AssignOperator::ShiftRight:
return Builtins::RightShiftAssignWith;
}
}
auto TypeChecker::TypeCheckStmt(Nonnull<Statement*> s,
const ImplScope& impl_scope)
-> ErrorOr<Success> {
@@ -4232,11 +4262,38 @@ auto TypeChecker::TypeCheckStmt(Nonnull<Statement*> s,
return ProgramError(assign.source_loc())
<< "Cannot assign to rvalue '" << assign.lhs() << "'";
}
if (assign.op() == AssignOperator::Plain &&
IsSameType(&assign.lhs().static_type(), &assign.rhs().static_type(),
impl_scope)) {
// TODO: Interface lookup.
CARBON_ASSIGN_OR_RETURN(
Nonnull<Expression*> converted_rhs,
ImplicitlyConvert("assignment", impl_scope, &assign.rhs(),
&assign.lhs().static_type()));
assign.set_rhs(converted_rhs);
} else {
CARBON_ASSIGN_OR_RETURN(
Nonnull<Expression*> rewritten,
BuildBuiltinMethodCall(
impl_scope, &assign.lhs(),
BuiltinInterfaceName{
GetBuiltinInterfaceForAssignOperator(assign.op()),
{&assign.rhs().static_type()}},
BuiltinMethodCall{"Op", {&assign.rhs()}}));
assign.set_rewritten_form(rewritten);
}
return Success();
}
case StatementKind::IncrementDecrement: {
auto& inc_dec = cast<IncrementDecrement>(*s);
CARBON_ASSIGN_OR_RETURN(
Nonnull<Expression*> converted_rhs,
ImplicitlyConvert("assignment", impl_scope, &assign.rhs(),
&assign.lhs().static_type()));
assign.set_rhs(converted_rhs);
Nonnull<Expression*> rewritten,
BuildBuiltinMethodCall(
impl_scope, &inc_dec.argument(),
BuiltinInterfaceName{
inc_dec.is_increment() ? Builtins::Inc : Builtins::Dec, {}},
BuiltinMethodCall{"Op"}));
inc_dec.set_rewritten_form(rewritten);
return Success();
}
case StatementKind::ExpressionStatement: {
@@ -4380,6 +4437,7 @@ auto TypeChecker::ExpectReturnOnAllPaths(
case StatementKind::Run:
case StatementKind::Await:
case StatementKind::Assign:
case StatementKind::IncrementDecrement:
case StatementKind::ExpressionStatement:
case StatementKind::While:
case StatementKind::For:
+193 -169
View File
@@ -31,92 +31,104 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
%s AFTER_OPERAND
/* table-begin */
ABSTRACT "abstract"
ADDR "addr"
ALIAS "alias"
AMPERSAND "&"
AND "and"
API "api"
ARROW "->"
AS "as"
AUTO "auto"
AWAIT "__await"
BASE "base"
BOOL "bool"
BREAK "break"
CARET "^"
CASE "case"
CHOICE "choice"
CLASS "class"
COLON ":"
COLON_BANG ":!"
COMMA ","
CONSTRAINT "constraint"
CONTINUATION "__continuation"
CONTINUATION_TYPE "__Continuation"
CONTINUE "continue"
DEFAULT "default"
DESTRUCTOR "destructor"
DOUBLE_ARROW "=>"
ELSE "else"
EQUAL "="
EQUAL_EQUAL "=="
EXTENDS "extends"
EXTERNAL "external"
FALSE "false"
FN "fn"
FN_TYPE "__Fn"
FOR "for"
FORALL "forall"
GREATER ">"
GREATER_EQUAL ">="
GREATER_GREATER ">>"
IF "if"
IMPL "impl"
IMPORT "import"
IN "in"
INTERFACE "interface"
IS "is"
LEFT_CURLY_BRACE "{"
LEFT_PARENTHESIS "("
LEFT_SQUARE_BRACKET "["
LESS "<"
LESS_EQUAL "<="
LESS_LESS "<<"
LET "let"
LIBRARY "library"
MATCH "match"
MATCH_FIRST "__match_first"
MINUS "-"
MIX "__mix"
MIXIN "__mixin"
NOT "not"
NOT_EQUAL "!="
OR "or"
PACKAGE "package"
PERCENT "%"
PERIOD "."
PIPE "|"
PLUS "+"
RETURN "return"
RETURNED "returned"
RIGHT_CURLY_BRACE "}"
RIGHT_PARENTHESIS ")"
RIGHT_SQUARE_BRACKET "]"
RUN "__run"
SELF "Self"
SEMICOLON ";"
SLASH "/"
STRING "String"
THEN "then"
TRUE "true"
TYPE "type"
UNDERSCORE "_"
UNIMPL_EXAMPLE "__unimplemented_example_infix"
VAR "var"
VIRTUAL "virtual"
WHERE "where"
WHILE "while"
ABSTRACT "abstract"
ADDR "addr"
ALIAS "alias"
AMPERSAND "&"
AMPERSAND_EQUAL "&="
AND "and"
API "api"
ARROW "->"
AS "as"
AUTO "auto"
AWAIT "__await"
BASE "base"
BOOL "bool"
BREAK "break"
CARET "^"
CARET_EQUAL "^="
CASE "case"
CHOICE "choice"
CLASS "class"
COLON ":"
COLON_BANG ":!"
COMMA ","
CONSTRAINT "constraint"
CONTINUATION "__continuation"
CONTINUATION_TYPE "__Continuation"
CONTINUE "continue"
DEFAULT "default"
DESTRUCTOR "destructor"
DOUBLE_ARROW "=>"
ELSE "else"
EQUAL "="
EQUAL_EQUAL "=="
EXTENDS "extends"
EXTERNAL "external"
FALSE "false"
FN "fn"
FN_TYPE "__Fn"
FOR "for"
FORALL "forall"
GREATER ">"
GREATER_EQUAL ">="
GREATER_GREATER ">>"
GREATER_GREATER_EQUAL ">>="
IF "if"
IMPL "impl"
IMPORT "import"
IN "in"
INTERFACE "interface"
IS "is"
LEFT_CURLY_BRACE "{"
LEFT_PARENTHESIS "("
LEFT_SQUARE_BRACKET "["
LESS "<"
LESS_EQUAL "<="
LESS_LESS "<<"
LESS_LESS_EQUAL "<<="
LET "let"
LIBRARY "library"
MATCH "match"
MATCH_FIRST "__match_first"
MINUS "-"
MINUS_EQUAL "-="
MINUS_MINUS "--"
MIX "__mix"
MIXIN "__mixin"
NOT "not"
NOT_EQUAL "!="
OR "or"
PACKAGE "package"
PERCENT "%"
PERCENT_EQUAL "%="
PERIOD "."
PIPE "|"
PIPE_EQUAL "|="
PLUS "+"
PLUS_EQUAL "+="
PLUS_PLUS "++"
RETURN "return"
RETURNED "returned"
RIGHT_CURLY_BRACE "}"
RIGHT_PARENTHESIS ")"
RIGHT_SQUARE_BRACKET "]"
RUN "__run"
SELF "Self"
SEMICOLON ";"
SLASH "/"
SLASH_EQUAL "/="
STAR_EQUAL "*="
STRING "String"
THEN "then"
TRUE "true"
TYPE "type"
UNDERSCORE "_"
UNIMPL_EXAMPLE "__unimplemented_example_infix"
VAR "var"
VIRTUAL "virtual"
WHERE "where"
WHILE "while"
/* table-end */
/* This should be kept table-like, but isn't automatic due to spaces. */
@@ -140,89 +152,101 @@ operand_start [(A-Za-z0-9_\"]
%}
/* table-begin */
{ABSTRACT} { return CARBON_SIMPLE_TOKEN(ABSTRACT); }
{ADDR} { return CARBON_SIMPLE_TOKEN(ADDR); }
{ALIAS} { return CARBON_SIMPLE_TOKEN(ALIAS); }
{AMPERSAND} { return CARBON_SIMPLE_TOKEN(AMPERSAND); }
{AND} { return CARBON_SIMPLE_TOKEN(AND); }
{API} { return CARBON_SIMPLE_TOKEN(API); }
{ARROW} { return CARBON_SIMPLE_TOKEN(ARROW); }
{AS} { return CARBON_SIMPLE_TOKEN(AS); }
{AUTO} { return CARBON_SIMPLE_TOKEN(AUTO); }
{AWAIT} { return CARBON_SIMPLE_TOKEN(AWAIT); }
{BASE} { return CARBON_SIMPLE_TOKEN(BASE); }
{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); }
{COLON_BANG} { return CARBON_SIMPLE_TOKEN(COLON_BANG); }
{COLON} { return CARBON_SIMPLE_TOKEN(COLON); }
{COMMA} { return CARBON_SIMPLE_TOKEN(COMMA); }
{CONSTRAINT} { return CARBON_SIMPLE_TOKEN(CONSTRAINT); }
{CONTINUATION_TYPE} { return CARBON_SIMPLE_TOKEN(CONTINUATION_TYPE); }
{CONTINUATION} { return CARBON_SIMPLE_TOKEN(CONTINUATION); }
{CONTINUE} { return CARBON_SIMPLE_TOKEN(CONTINUE); }
{DEFAULT} { return CARBON_SIMPLE_TOKEN(DEFAULT); }
{DESTRUCTOR} { return CARBON_SIMPLE_TOKEN(DESTRUCTOR); }
{DOUBLE_ARROW} { return CARBON_SIMPLE_TOKEN(DOUBLE_ARROW); }
{ELSE} { return CARBON_SIMPLE_TOKEN(ELSE); }
{EQUAL_EQUAL} { return CARBON_SIMPLE_TOKEN(EQUAL_EQUAL); }
{EQUAL} { return CARBON_SIMPLE_TOKEN(EQUAL); }
{EXTENDS} { return CARBON_SIMPLE_TOKEN(EXTENDS); }
{EXTERNAL} { return CARBON_SIMPLE_TOKEN(EXTERNAL); }
{FALSE} { return CARBON_SIMPLE_TOKEN(FALSE); }
{FN_TYPE} { return CARBON_SIMPLE_TOKEN(FN_TYPE); }
{FN} { return CARBON_SIMPLE_TOKEN(FN); }
{FORALL} { return CARBON_SIMPLE_TOKEN(FORALL); }
{FOR} { return CARBON_SIMPLE_TOKEN(FOR); }
{GREATER_EQUAL} { return CARBON_SIMPLE_TOKEN(GREATER_EQUAL); }
{GREATER_GREATER} { return CARBON_SIMPLE_TOKEN(GREATER_GREATER); }
{GREATER} { return CARBON_SIMPLE_TOKEN(GREATER); }
{IF} { return CARBON_SIMPLE_TOKEN(IF); }
{IMPL} { return CARBON_SIMPLE_TOKEN(IMPL); }
{IMPORT} { return CARBON_SIMPLE_TOKEN(IMPORT); }
{INTERFACE} { return CARBON_SIMPLE_TOKEN(INTERFACE); }
{IN} { return CARBON_SIMPLE_TOKEN(IN); }
{IS} { return CARBON_SIMPLE_TOKEN(IS); }
{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_EQUAL} { return CARBON_SIMPLE_TOKEN(LESS_EQUAL); }
{LESS_LESS} { return CARBON_SIMPLE_TOKEN(LESS_LESS); }
{LESS} { return CARBON_SIMPLE_TOKEN(LESS); }
{LET} { return CARBON_SIMPLE_TOKEN(LET); }
{LIBRARY} { return CARBON_SIMPLE_TOKEN(LIBRARY); }
{MATCH_FIRST} { return CARBON_SIMPLE_TOKEN(MATCH_FIRST); }
{MATCH} { return CARBON_SIMPLE_TOKEN(MATCH); }
{MINUS} { return CARBON_SIMPLE_TOKEN(MINUS); }
{MIXIN} { return CARBON_SIMPLE_TOKEN(MIXIN); }
{MIX} { return CARBON_SIMPLE_TOKEN(MIX); }
{NOT_EQUAL} { return CARBON_SIMPLE_TOKEN(NOT_EQUAL); }
{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); }
{RUN} { return CARBON_SIMPLE_TOKEN(RUN); }
{SELF} { return CARBON_SIMPLE_TOKEN(SELF); }
{SEMICOLON} { return CARBON_SIMPLE_TOKEN(SEMICOLON); }
{SLASH} { return CARBON_SIMPLE_TOKEN(SLASH); }
{STRING} { return CARBON_SIMPLE_TOKEN(STRING); }
{THEN} { return CARBON_SIMPLE_TOKEN(THEN); }
{TRUE} { return CARBON_SIMPLE_TOKEN(TRUE); }
{TYPE} { return CARBON_SIMPLE_TOKEN(TYPE); }
{UNDERSCORE} { return CARBON_SIMPLE_TOKEN(UNDERSCORE); }
{UNIMPL_EXAMPLE} { return CARBON_SIMPLE_TOKEN(UNIMPL_EXAMPLE); }
{VAR} { return CARBON_SIMPLE_TOKEN(VAR); }
{VIRTUAL} { return CARBON_SIMPLE_TOKEN(VIRTUAL); }
{WHERE} { return CARBON_SIMPLE_TOKEN(WHERE); }
{WHILE} { return CARBON_SIMPLE_TOKEN(WHILE); }
{ABSTRACT} { return CARBON_SIMPLE_TOKEN(ABSTRACT); }
{ADDR} { return CARBON_SIMPLE_TOKEN(ADDR); }
{ALIAS} { return CARBON_SIMPLE_TOKEN(ALIAS); }
{AMPERSAND_EQUAL} { return CARBON_SIMPLE_TOKEN(AMPERSAND_EQUAL); }
{AMPERSAND} { return CARBON_SIMPLE_TOKEN(AMPERSAND); }
{AND} { return CARBON_SIMPLE_TOKEN(AND); }
{API} { return CARBON_SIMPLE_TOKEN(API); }
{ARROW} { return CARBON_SIMPLE_TOKEN(ARROW); }
{AS} { return CARBON_SIMPLE_TOKEN(AS); }
{AUTO} { return CARBON_SIMPLE_TOKEN(AUTO); }
{AWAIT} { return CARBON_SIMPLE_TOKEN(AWAIT); }
{BASE} { return CARBON_SIMPLE_TOKEN(BASE); }
{BOOL} { return CARBON_SIMPLE_TOKEN(BOOL); }
{BREAK} { return CARBON_SIMPLE_TOKEN(BREAK); }
{CARET_EQUAL} { return CARBON_SIMPLE_TOKEN(CARET_EQUAL); }
{CARET} { return CARBON_SIMPLE_TOKEN(CARET); }
{CASE} { return CARBON_SIMPLE_TOKEN(CASE); }
{CHOICE} { return CARBON_SIMPLE_TOKEN(CHOICE); }
{CLASS} { return CARBON_SIMPLE_TOKEN(CLASS); }
{COLON_BANG} { return CARBON_SIMPLE_TOKEN(COLON_BANG); }
{COLON} { return CARBON_SIMPLE_TOKEN(COLON); }
{COMMA} { return CARBON_SIMPLE_TOKEN(COMMA); }
{CONSTRAINT} { return CARBON_SIMPLE_TOKEN(CONSTRAINT); }
{CONTINUATION_TYPE} { return CARBON_SIMPLE_TOKEN(CONTINUATION_TYPE); }
{CONTINUATION} { return CARBON_SIMPLE_TOKEN(CONTINUATION); }
{CONTINUE} { return CARBON_SIMPLE_TOKEN(CONTINUE); }
{DEFAULT} { return CARBON_SIMPLE_TOKEN(DEFAULT); }
{DESTRUCTOR} { return CARBON_SIMPLE_TOKEN(DESTRUCTOR); }
{DOUBLE_ARROW} { return CARBON_SIMPLE_TOKEN(DOUBLE_ARROW); }
{ELSE} { return CARBON_SIMPLE_TOKEN(ELSE); }
{EQUAL_EQUAL} { return CARBON_SIMPLE_TOKEN(EQUAL_EQUAL); }
{EQUAL} { return CARBON_SIMPLE_TOKEN(EQUAL); }
{EXTENDS} { return CARBON_SIMPLE_TOKEN(EXTENDS); }
{EXTERNAL} { return CARBON_SIMPLE_TOKEN(EXTERNAL); }
{FALSE} { return CARBON_SIMPLE_TOKEN(FALSE); }
{FN_TYPE} { return CARBON_SIMPLE_TOKEN(FN_TYPE); }
{FN} { return CARBON_SIMPLE_TOKEN(FN); }
{FORALL} { return CARBON_SIMPLE_TOKEN(FORALL); }
{FOR} { return CARBON_SIMPLE_TOKEN(FOR); }
{GREATER_EQUAL} { return CARBON_SIMPLE_TOKEN(GREATER_EQUAL); }
{GREATER_GREATER_EQUAL} { return CARBON_SIMPLE_TOKEN(GREATER_GREATER_EQUAL); }
{GREATER_GREATER} { return CARBON_SIMPLE_TOKEN(GREATER_GREATER); }
{GREATER} { return CARBON_SIMPLE_TOKEN(GREATER); }
{IF} { return CARBON_SIMPLE_TOKEN(IF); }
{IMPL} { return CARBON_SIMPLE_TOKEN(IMPL); }
{IMPORT} { return CARBON_SIMPLE_TOKEN(IMPORT); }
{INTERFACE} { return CARBON_SIMPLE_TOKEN(INTERFACE); }
{IN} { return CARBON_SIMPLE_TOKEN(IN); }
{IS} { return CARBON_SIMPLE_TOKEN(IS); }
{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_EQUAL} { return CARBON_SIMPLE_TOKEN(LESS_EQUAL); }
{LESS_LESS_EQUAL} { return CARBON_SIMPLE_TOKEN(LESS_LESS_EQUAL); }
{LESS_LESS} { return CARBON_SIMPLE_TOKEN(LESS_LESS); }
{LESS} { return CARBON_SIMPLE_TOKEN(LESS); }
{LET} { return CARBON_SIMPLE_TOKEN(LET); }
{LIBRARY} { return CARBON_SIMPLE_TOKEN(LIBRARY); }
{MATCH_FIRST} { return CARBON_SIMPLE_TOKEN(MATCH_FIRST); }
{MATCH} { return CARBON_SIMPLE_TOKEN(MATCH); }
{MINUS_EQUAL} { return CARBON_SIMPLE_TOKEN(MINUS_EQUAL); }
{MINUS_MINUS} { return CARBON_SIMPLE_TOKEN(MINUS_MINUS); }
{MINUS} { return CARBON_SIMPLE_TOKEN(MINUS); }
{MIXIN} { return CARBON_SIMPLE_TOKEN(MIXIN); }
{MIX} { return CARBON_SIMPLE_TOKEN(MIX); }
{NOT_EQUAL} { return CARBON_SIMPLE_TOKEN(NOT_EQUAL); }
{NOT} { return CARBON_SIMPLE_TOKEN(NOT); }
{OR} { return CARBON_SIMPLE_TOKEN(OR); }
{PACKAGE} { return CARBON_SIMPLE_TOKEN(PACKAGE); }
{PERCENT_EQUAL} { return CARBON_SIMPLE_TOKEN(PERCENT_EQUAL); }
{PERCENT} { return CARBON_SIMPLE_TOKEN(PERCENT); }
{PERIOD} { return CARBON_SIMPLE_TOKEN(PERIOD); }
{PIPE_EQUAL} { return CARBON_SIMPLE_TOKEN(PIPE_EQUAL); }
{PIPE} { return CARBON_SIMPLE_TOKEN(PIPE); }
{PLUS_EQUAL} { return CARBON_SIMPLE_TOKEN(PLUS_EQUAL); }
{PLUS_PLUS} { return CARBON_SIMPLE_TOKEN(PLUS_PLUS); }
{PLUS} { return CARBON_SIMPLE_TOKEN(PLUS); }
{RETURNED} { return CARBON_SIMPLE_TOKEN(RETURNED); }
{RETURN} { return CARBON_SIMPLE_TOKEN(RETURN); }
{RUN} { return CARBON_SIMPLE_TOKEN(RUN); }
{SELF} { return CARBON_SIMPLE_TOKEN(SELF); }
{SEMICOLON} { return CARBON_SIMPLE_TOKEN(SEMICOLON); }
{SLASH_EQUAL} { return CARBON_SIMPLE_TOKEN(SLASH_EQUAL); }
{SLASH} { return CARBON_SIMPLE_TOKEN(SLASH); }
{STAR_EQUAL} { return CARBON_SIMPLE_TOKEN(STAR_EQUAL); }
{STRING} { return CARBON_SIMPLE_TOKEN(STRING); }
{THEN} { return CARBON_SIMPLE_TOKEN(THEN); }
{TRUE} { return CARBON_SIMPLE_TOKEN(TRUE); }
{TYPE} { return CARBON_SIMPLE_TOKEN(TYPE); }
{UNDERSCORE} { return CARBON_SIMPLE_TOKEN(UNDERSCORE); }
{UNIMPL_EXAMPLE} { return CARBON_SIMPLE_TOKEN(UNIMPL_EXAMPLE); }
{VAR} { return CARBON_SIMPLE_TOKEN(VAR); }
{VIRTUAL} { return CARBON_SIMPLE_TOKEN(VIRTUAL); }
{WHERE} { return CARBON_SIMPLE_TOKEN(WHERE); }
{WHILE} { return CARBON_SIMPLE_TOKEN(WHILE); }
/* table-end */
/* More modern Bisons provide make_EOF. */
+48 -2
View File
@@ -121,6 +121,8 @@
%type <std::vector<Nonnull<Declaration*>>> interface_body
%type <std::vector<Nonnull<Declaration*>>> impl_body
%type <Nonnull<Statement*>> statement
%type <Nonnull<Statement*>> assign_statement
%type <AssignOperator> assign_operator
%type <Nonnull<If*>> if_statement
%type <std::optional<Nonnull<Block*>>> optional_else
%type <std::pair<Nonnull<Expression*>, bool>> return_expression
@@ -207,6 +209,7 @@
ADDR
ALIAS
AMPERSAND
AMPERSAND_EQUAL
AND
API
ARROW
@@ -217,6 +220,7 @@
BOOL
BREAK
CARET
CARET_EQUAL
CASE
CHOICE
CLASS
@@ -243,6 +247,7 @@
GREATER
GREATER_EQUAL
GREATER_GREATER
GREATER_GREATER_EQUAL
IF
IMPL
IMPORT
@@ -255,21 +260,29 @@
LESS
LESS_EQUAL
LESS_LESS
LESS_LESS_EQUAL
LET
LIBRARY
MATCH
MATCH_FIRST
MINUS
MINUS_EQUAL
MINUS_MINUS
MIX
MIXIN
NOT
NOT_EQUAL
OR
OR_EQUAL
PACKAGE
PERCENT
PERCENT_EQUAL
PERIOD
PIPE
PIPE_EQUAL
PLUS
PLUS_EQUAL
PLUS_PLUS
RETURN
RETURNED
RIGHT_CURLY_BRACE
@@ -279,6 +292,8 @@
SELF
SEMICOLON
SLASH
SLASH_EQUAL
STAR_EQUAL
STRING
THEN
TRUE
@@ -927,8 +942,7 @@ clause_list:
}
;
statement:
statement_expression EQUAL expression SEMICOLON
{ $$ = arena->New<Assign>(context.source_loc(), $1, $3); }
assign_statement
| VAR pattern SEMICOLON
{
$$ = arena->New<VariableDefinition>(
@@ -989,6 +1003,38 @@ statement:
| FOR LEFT_PARENTHESIS variable_declaration IN type_expression RIGHT_PARENTHESIS block
{ $$ = arena->New<For>(context.source_loc(), $3, $5, $7); }
;
assign_statement:
statement_expression assign_operator expression SEMICOLON
{ $$ = arena->New<Assign>(context.source_loc(), $1, $2, $3); }
| PLUS_PLUS expression SEMICOLON
{ $$ = arena->New<IncrementDecrement>(context.source_loc(), $2, true); }
| MINUS_MINUS expression SEMICOLON
{ $$ = arena->New<IncrementDecrement>(context.source_loc(), $2, false); }
;
assign_operator:
EQUAL
{ $$ = AssignOperator::Plain; }
| PLUS_EQUAL
{ $$ = AssignOperator::Add; }
| SLASH_EQUAL
{ $$ = AssignOperator::Div; }
| STAR_EQUAL
{ $$ = AssignOperator::Mul; }
| PERCENT_EQUAL
{ $$ = AssignOperator::Mod; }
| MINUS_EQUAL
{ $$ = AssignOperator::Sub; }
| AMPERSAND_EQUAL
{ $$ = AssignOperator::And; }
| PIPE_EQUAL
{ $$ = AssignOperator::Or; }
| CARET_EQUAL
{ $$ = AssignOperator::Xor; }
| LESS_LESS_EQUAL
{ $$ = AssignOperator::ShiftLeft; }
| GREATER_GREATER_EQUAL
{ $$ = AssignOperator::ShiftRight; }
;
if_statement:
IF LEFT_PARENTHESIS expression RIGHT_PARENTHESIS block optional_else
{ $$ = arena->New<If>(context.source_loc(), $3, $5, $6); }
+1 -1
View File
@@ -10,7 +10,7 @@ package ExplorerTest api;
fn Main() -> i32 {
// error
// CHECK:STDERR: SYNTAX ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_missing_var.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting SLASH or binary *
// CHECK:STDERR: SYNTAX ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_missing_var.carbon:[[@LINE+1]]: syntax error, unexpected COLON
x : i32;
return 1;
}
+1 -1
View File
@@ -12,7 +12,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:STDERR: SYNTAX ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_var_named_self.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting SLASH or binary *
// CHECK:STDERR: SYNTAX ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_var_named_self.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting EQUAL or SEMICOLON
var Self : i32 = 0;
return Self;
}
+1 -1
View File
@@ -10,7 +10,7 @@ package ExplorerTest api;
fn Main() -> i32 {
// Error: Can't use keyword `Self` as the name of a local.
// CHECK:STDERR: SYNTAX ERROR: {{.*}}/explorer/testdata/let/fail_local_named_self.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting SLASH or binary *
// CHECK:STDERR: SYNTAX ERROR: {{.*}}/explorer/testdata/let/fail_local_named_self.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting EQUAL
let Self: auto = 10;
return 0;
}
+10 -2
View File
@@ -5,7 +5,10 @@
// AUTOUPDATE
// RUN: %{explorer-run}
// RUN: %{explorer-run-trace}
// CHECK:STDOUT: result: 12
// CHECK:STDOUT: 12
// CHECK:STDOUT: 19
// CHECK:STDOUT: 26
// CHECK:STDOUT: result: 0
package ExplorerTest api;
@@ -18,5 +21,10 @@ external impl A as Add {
fn Main() -> i32 {
var a: A = {.n = 5};
var b: A = {.n = 7};
return (a + b).n;
a = a + b;
Print("{0}", a.n);
a += b;
Print("{0}", a.n);
Print("{0}", (a + b).n);
return 0;
}
+27
View File
@@ -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
//
// AUTOUPDATE
// RUN: %{explorer-run}
// RUN: %{explorer-run-trace}
// CHECK:STDOUT: Before: 1
// CHECK:STDOUT: Interface: 2
// CHECK:STDOUT: Op: 3
// CHECK:STDOUT: result: 0
package ExplorerTest api;
class C {
var n: i32;
}
fn Main() -> i32 {
var c: C = {.n = 1};
Print("Before: {0}", c.n);
c.(AssignWith({.n: i32}).Op)({.n = 2});
Print("Interface: {0}", c.n);
c = {.n = 3};
Print("Op: {0}", c.n);
return 0;
}
+11 -4
View File
@@ -5,7 +5,10 @@
// AUTOUPDATE
// RUN: %{explorer-run}
// RUN: %{explorer-run-trace}
// CHECK:STDOUT: result: 1
// CHECK:STDOUT: 5
// CHECK:STDOUT: 4
// CHECK:STDOUT: 0
// CHECK:STDOUT: result: 0
package ExplorerTest api;
@@ -16,7 +19,11 @@ external impl A as BitAndWith(i32) where .Result = A {
}
fn Main() -> i32 {
var a: A = {.n = 5};
a = a & 1;
return a.n;
var a: A = {.n = 13};
a = a & 7;
Print("{0}", a.n);
a &= -2;
Print("{0}", a.n);
Print("{0}", (a & 3).n);
return 0;
}
+11 -4
View File
@@ -5,7 +5,10 @@
// AUTOUPDATE
// RUN: %{explorer-run}
// RUN: %{explorer-run-trace}
// CHECK:STDOUT: result: 5
// CHECK:STDOUT: 3
// CHECK:STDOUT: 7
// CHECK:STDOUT: 15
// CHECK:STDOUT: result: 0
package ExplorerTest api;
@@ -16,7 +19,11 @@ external impl A as BitOrWith(i32) where .Result = A {
}
fn Main() -> i32 {
var a: A = {.n = 4};
a = a | 1;
return a.n;
var a: A = {.n = 1};
a = a | 2;
Print("{0}", a.n);
a |= 5;
Print("{0}", a.n);
Print("{0}", (a | 12).n);
return 0;
}
+10 -3
View File
@@ -5,7 +5,10 @@
// AUTOUPDATE
// RUN: %{explorer-run}
// RUN: %{explorer-run-trace}
// CHECK:STDOUT: result: 4
// CHECK:STDOUT: 6
// CHECK:STDOUT: 0
// CHECK:STDOUT: -1
// CHECK:STDOUT: result: 0
package ExplorerTest api;
@@ -17,6 +20,10 @@ external impl A as BitXorWith(i32) where .Result = A {
fn Main() -> i32 {
var a: A = {.n = 5};
a = a ^ 1;
return a.n;
a = a ^ 3;
Print("{0}", a.n);
a ^= 6;
Print("{0}", a.n);
Print("{0}", (a ^ -1).n);
return 0;
}
+10 -3
View File
@@ -5,7 +5,10 @@
// AUTOUPDATE
// RUN: %{explorer-run}
// RUN: %{explorer-run-trace}
// CHECK:STDOUT: result: 2
// CHECK:STDOUT: 6
// CHECK:STDOUT: 3
// CHECK:STDOUT: 1
// CHECK:STDOUT: result: 0
package ExplorerTest api;
@@ -16,7 +19,11 @@ external impl A as DivWith(i32) where .Result = A {
}
fn Main() -> i32 {
var a: A = {.n = 8};
var a: A = {.n = 19};
a = a / 3;
return a.n;
Print("{0}", a.n);
a /= 2;
Print("{0}", a.n);
Print("{0}", (a / 2).n);
return 0;
}
+48
View File
@@ -0,0 +1,48 @@
// 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
//
// AUTOUPDATE
// RUN: %{explorer-run}
// RUN: %{explorer-run-trace}
// CHECK:STDOUT: 6
// CHECK:STDOUT: 5
// CHECK:STDOUT: 6
// CHECK:STDOUT: 5
// CHECK:STDOUT: 6
// CHECK:STDOUT: 5
// CHECK:STDOUT: 6
// CHECK:STDOUT: 5
// CHECK:STDOUT: result: 0
package ExplorerTest api;
class A { var n: i32; }
external impl A as Inc {
fn Op[addr self: Self*]() { ++self->n; }
}
external impl A as Dec {
fn Op[addr self: Self*]() { --self->n; }
}
fn Main() -> i32 {
var a: A = {.n = 5};
++a.n;
Print("{0}", a.n);
--a.n;
Print("{0}", a.n);
++a;
Print("{0}", a.n);
--a;
Print("{0}", a.n);
a.n.(Inc.Op)();
Print("{0}", a.n);
a.n.(Dec.Op)();
Print("{0}", a.n);
a.(Inc.Op)();
Print("{0}", a.n);
a.(Dec.Op)();
Print("{0}", a.n);
return 0;
}
+11 -4
View File
@@ -5,7 +5,10 @@
// AUTOUPDATE
// RUN: %{explorer-run}
// RUN: %{explorer-run-trace}
// CHECK:STDOUT: result: 1
// CHECK:STDOUT: 7
// CHECK:STDOUT: 3
// CHECK:STDOUT: 0
// CHECK:STDOUT: result: 0
package ExplorerTest api;
@@ -16,7 +19,11 @@ external impl A as ModWith(i32) where .Result = A {
}
fn Main() -> i32 {
var a: A = {.n = 5};
a = a % 2;
return a.n;
var a: A = {.n = 15};
a = a % 8;
Print("{0}", a.n);
a %= 4;
Print("{0}", a.n);
Print("{0}", (a % 3).n);
return 0;
}
+9 -2
View File
@@ -5,7 +5,10 @@
// AUTOUPDATE
// RUN: %{explorer-run}
// RUN: %{explorer-run-trace}
// CHECK:STDOUT: result: 10
// CHECK:STDOUT: 10
// CHECK:STDOUT: 30
// CHECK:STDOUT: 210
// CHECK:STDOUT: result: 0
package ExplorerTest api;
@@ -18,5 +21,9 @@ external impl A as MulWith(i32) where .Result = A {
fn Main() -> i32 {
var a: A = {.n = 5};
a = a * 2;
return a.n;
Print("{0}", a.n);
a *= 3;
Print("{0}", a.n);
Print("{0}", (a * 7).n);
return 0;
}
+8
View File
@@ -5,6 +5,8 @@
// AUTOUPDATE
// RUN: %{explorer-run}
// RUN: %{explorer-run-trace}
// CHECK:STDOUT: 8
// CHECK:STDOUT: 4
// CHECK:STDOUT: result: 0
package ExplorerTest api;
@@ -21,5 +23,11 @@ fn Main() -> i32 {
if (not (-1 >> 1 == -1)) { return 9; }
if (not (-2 >> 1 == -1)) { return 10; }
var n: i32 = 1;
n <<= 3;
Print("{0}", n);
n >>= 1;
Print("{0}", n);
return 0;
}
+9 -2
View File
@@ -5,7 +5,10 @@
// AUTOUPDATE
// RUN: %{explorer-run}
// RUN: %{explorer-run-trace}
// CHECK:STDOUT: result: 4
// CHECK:STDOUT: 4
// CHECK:STDOUT: 3
// CHECK:STDOUT: 2
// CHECK:STDOUT: result: 0
package ExplorerTest api;
@@ -18,5 +21,9 @@ external impl A as SubWith(i32) where .Result = A {
fn Main() -> i32 {
var a: A = {.n = 5};
a = a - 1;
return a.n;
Print("{0}", a.n);
a -= 1;
Print("{0}", a.n);
Print("{0}", (a - 1).n);
return 0;
}
@@ -10,7 +10,7 @@ package ExplorerTest api;
fn Main() -> i32 {
var p: auto = {.x = 0, .y = 0};
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/struct/fail_assign_different_types.carbon:[[@LINE+1]]: type error in assignment: '{.y: i32}' is not implicitly convertible to '{.x: i32, .y: i32}'
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/struct/fail_assign_different_types.carbon:[[@LINE+1]]: could not find implementation of interface AssignWith(U = {.y: i32}) for {.x: i32, .y: i32}
p = {.y = 0};
return 0;
}
@@ -0,0 +1,16 @@
// 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
//
// AUTOUPDATE
// RUN: %{not} %{explorer-run}
// RUN: %{not} %{explorer-run-trace}
package ExplorerTest api;
fn Main() -> i32 {
var x: i32;
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/unformed/static/fail_compound_assign.carbon:[[@LINE+1]]: use of uninitialized variable x
x += 1;
return x;
}
+16
View File
@@ -0,0 +1,16 @@
// 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
//
// AUTOUPDATE
// RUN: %{not} %{explorer-run}
// RUN: %{not} %{explorer-run-trace}
package ExplorerTest api;
fn Main() -> i32 {
var x: i32;
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/unformed/static/fail_increment.carbon:[[@LINE+1]]: use of uninitialized variable x
++x;
return x;
}