mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
Pointers (#1060)
* 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:
@@ -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 "-";
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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); }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user