* Naive pointer implementation

* Bug fixes and better pointer tests

* Remove debug print statement

* Implement changes suggested by @geoffromer

Also add a failing test case that tests applying the address-of operator
to an rvalue.
This commit is contained in:
Darshal Shetty
2022-02-05 10:27:54 -05:00
committed by GitHub
parent f6cbd2231e
commit 4479c55305
11 changed files with 131 additions and 5 deletions
+2
View File
@@ -54,6 +54,8 @@ auto ToString(Operator op) -> std::string_view {
switch (op) {
case Operator::Add:
return "+";
case Operator::AddressOf:
return "&";
case Operator::Neg:
case Operator::Sub:
return "-";
+1
View File
@@ -96,6 +96,7 @@ class FieldInitializer {
enum class Operator {
Add,
AddressOf,
And,
Deref,
Eq,
@@ -154,7 +154,9 @@ auto Interpreter::EvalPrim(Operator op,
case Operator::Ptr:
return arena_->New<PointerType>(args[0]);
case Operator::Deref:
FATAL() << "dereference not implemented yet";
return heap_.Read(cast<PointerValue>(*args[0]).address(), source_loc);
case Operator::AddressOf:
return arena_->New<PointerValue>(cast<LValue>(*args[0]).address());
}
}
@@ -313,13 +315,26 @@ void Interpreter::StepLvalue() {
return todo_.FinishAction(arena_->New<LValue>(field));
}
}
case ExpressionKind::PrimitiveOperatorExpression: {
const PrimitiveOperatorExpression& op = cast<PrimitiveOperatorExpression>(exp);
if (op.op() != Operator::Deref) {
FATAL() << "Can't treat primitive operator expression as lvalue: " << exp;
}
if (act.pos() == 0) {
return todo_.Spawn(
std::make_unique<ExpressionAction>(op.arguments()[0]));
} else {
const PointerValue& res = cast<PointerValue>(*act.results()[0]);
return todo_.FinishAction(arena_->New<LValue>(res.address()));
}
break;
}
case ExpressionKind::TupleLiteral:
case ExpressionKind::StructLiteral:
case ExpressionKind::StructTypeLiteral:
case ExpressionKind::IntLiteral:
case ExpressionKind::BoolLiteral:
case ExpressionKind::CallExpression:
case ExpressionKind::PrimitiveOperatorExpression:
case ExpressionKind::IntTypeLiteral:
case ExpressionKind::BoolTypeLiteral:
case ExpressionKind::TypeTypeLiteral:
@@ -340,6 +355,7 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
switch (value->kind()) {
case Value::Kind::IntValue:
case Value::Kind::FunctionValue:
case Value::Kind::PointerValue:
case Value::Kind::LValue:
case Value::Kind::BoolValue:
case Value::Kind::NominalClassValue:
@@ -509,7 +525,11 @@ void Interpreter::StepExp() {
// { {v :: op(vs,[],e,es) :: C, E, F} :: S, H}
// -> { {e :: op(vs,v,[],es) :: C, E, F} :: S, H}
Nonnull<const Expression*> arg = op.arguments()[act.pos()];
return todo_.Spawn(std::make_unique<ExpressionAction>(arg));
if (op.op() == Operator::AddressOf) {
return todo_.Spawn(std::make_unique<LValAction>(arg));
} else {
return todo_.Spawn(std::make_unique<ExpressionAction>(arg));
}
} else {
// { {v :: op(vs,[]) :: C, E, F} :: S, H}
// -> { {eval_prim(op, (vs,v)) :: C, E, F} :: S, H}
@@ -74,6 +74,7 @@ static auto IsConcreteType(Nonnull<const Value*> value) -> bool {
switch (value->kind()) {
case Value::Kind::IntValue:
case Value::Kind::FunctionValue:
case Value::Kind::PointerValue:
case Value::Kind::LValue:
case Value::Kind::BoolValue:
case Value::Kind::StructValue:
@@ -313,6 +314,7 @@ void TypeChecker::ArgumentDeduction(
case Value::Kind::IntValue:
case Value::Kind::BoolValue:
case Value::Kind::FunctionValue:
case Value::Kind::PointerValue:
case Value::Kind::LValue:
case Value::Kind::StructValue:
case Value::Kind::NominalClassValue:
@@ -378,6 +380,7 @@ auto TypeChecker::Substitute(
case Value::Kind::IntValue:
case Value::Kind::BoolValue:
case Value::Kind::FunctionValue:
case Value::Kind::PointerValue:
case Value::Kind::LValue:
case Value::Kind::StructValue:
case Value::Kind::NominalClassValue:
@@ -618,6 +621,14 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e) {
SetStaticType(&op, arena_->New<TypeType>());
op.set_value_category(ValueCategory::Let);
return;
case Operator::AddressOf:
if (op.arguments()[0]->value_category() != ValueCategory::Var) {
FATAL_COMPILATION_ERROR(op.arguments()[0]->source_loc()) <<
"Argument to " << ToString(op.op()) << " should be an lvalue.";
}
SetStaticType(&op, arena_->New<PointerType>(ts[0]));
op.set_value_category(ValueCategory::Let);
return;
}
break;
}
+5 -1
View File
@@ -179,8 +179,11 @@ void Value::Print(llvm::raw_ostream& out) const {
case Value::Kind::FunctionValue:
out << "fun<" << cast<FunctionValue>(*this).declaration().name() << ">";
break;
case Value::Kind::PointerValue:
out << "ptr<" << cast<PointerValue>(*this).address() << ">";
break;
case Value::Kind::LValue:
out << "ptr<" << cast<LValue>(*this).address() << ">";
out << "lval<" << cast<LValue>(*this).address() << ">";
break;
case Value::Kind::BoolType:
out << "Bool";
@@ -434,6 +437,7 @@ auto ValueEqual(Nonnull<const Value*> v1, Nonnull<const Value*> v2) -> bool {
case Value::Kind::BindingPlaceholderValue:
case Value::Kind::AlternativeConstructorValue:
case Value::Kind::ContinuationValue:
case Value::Kind::PointerValue:
case Value::Kind::LValue:
// TODO: support pointer comparisons once we have a clearer distinction
// between pointers and lvalues.
+17
View File
@@ -36,6 +36,7 @@ class Value {
enum class Kind {
IntValue,
FunctionValue,
PointerValue,
LValue,
BoolValue,
StructValue,
@@ -150,6 +151,22 @@ class LValue : public Value {
Address value_;
};
// A pointer value
class PointerValue : public Value {
public:
explicit PointerValue(Address value)
: Value(Kind::PointerValue), value_(std::move(value)) {}
static auto classof(const Value* value) -> bool {
return value->kind() == Kind::PointerValue;
}
auto address() const -> const Address& { return value_; }
private:
Address value_;
};
// A bool value.
class BoolValue : public Value {
public:
+2
View File
@@ -38,6 +38,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
%s AFTER_OPERAND
/* table-begin */
AMPERSAND "&"
AND "and"
API "api"
ARROW "->"
@@ -134,6 +135,7 @@ string_literal \"([^\\\"\n\v\f\r]|\\.)*\"
%}
/* table-begin */
{AMPERSAND} { return SIMPLE_TOKEN(AMPERSAND); }
{AND} { return SIMPLE_TOKEN(AND); }
{API} { return SIMPLE_TOKEN(API); }
{ARROW} { return SIMPLE_TOKEN(ARROW); }
+8 -1
View File
@@ -141,6 +141,7 @@
%token
// Most tokens have their spelling defined in lexer.lpp.
// table-begin
AMPERSAND
AND
API
ARROW
@@ -214,7 +215,7 @@
%nonassoc EQUAL_EQUAL
%left PLUS MINUS
%left BINARY_STAR
%precedence NOT UNARY_MINUS PREFIX_STAR
%precedence NOT UNARY_MINUS PREFIX_STAR AMPERSAND
// We need to give the `UNARY_STAR` token a precedence, rather than overriding
// the precedence of the `expression UNARY_STAR` rule below, because bison
// compares the precedence of the final token (for a shift) to the precedence
@@ -367,6 +368,12 @@ expression:
context.source_loc(), Operator::Deref,
std::vector<Nonnull<Expression*>>({$2}));
}
| AMPERSAND expression
{
$$ = arena->New<PrimitiveOperatorExpression>(
context.source_loc(), Operator::AddressOf,
std::vector<Nonnull<Expression*>>({$2}));
}
| expression tuple
{ $$ = arena->New<CallExpression>(context.source_loc(), $1, $2); }
| expression POSTFIX_STAR
+24
View File
@@ -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: %{executable_semantics} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{executable_semantics} --trace %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{executable_semantics} %s
// CHECK: result: 0
package ExecutableSemanticsTest api;
fn Main() -> i32 {
var x: i32 = 5;
x = 10; // changes x to 10
var p: i32* = &x;
*p = 7; // changes x to 7
var q: i32* = &*p;
*q = 0; // changes x to 0
var y: i32 = *p;
return y;
}
@@ -0,0 +1,18 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// RUN: %{not} %{executable_semantics} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{not} %{executable_semantics} --trace %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{executable_semantics} %s
// CHECK: COMPILATION ERROR: {{.*}}/executable_semantics/testdata/pointer/fail_rvalue_addressof.carbon:16: Argument to & should be an lvalue.
package ExecutableSemanticsTest api;
fn Main() -> i32 {
var x: i32 = 5;
var p: i32* = &5;
return 0;
}
@@ -0,0 +1,20 @@
// 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: %{executable_semantics} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{executable_semantics} --trace %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{executable_semantics} %s
// CHECK: result: 0
package ExecutableSemanticsTest api;
fn Main() -> i32 {
var x: auto = {.x = 10, .y = 1};
var p: i32* = &x.x;
*p = 0;
return x.x;
}