Re-implement array assignment.
Re-implement array assignment.
1.1 --- a/src/Array.cpp Mon Dec 24 02:56:02 2012 -0800
1.2 +++ b/src/Array.cpp Mon Dec 24 16:09:13 2012 -0800
1.3 @@ -273,6 +273,18 @@
1.4 if (!Struct::Copy(zone, left, right))
1.5 return false;
1.6 }
1.7 + } else if (contained->isArray() && ArrayType::cast(contained)->isFixedLength()) {
1.8 + assert(ArrayType::cast(source->type())->fixedLength() ==
1.9 + ArrayType::cast(contained)->fixedLength());
1.10 +
1.11 + Local<Array> left(zone);
1.12 + Local<Array> right(zone);
1.13 + for (unsigned i = 0; i < source->length(); i++) {
1.14 + left = Array::cast(dest->readObject(i));
1.15 + right = Array::cast(source->readObject(i));
1.16 + if (!Array::ShallowCopy(zone, left, right))
1.17 + return false;
1.18 + }
1.19 } else {
1.20 assert(dest->length() == source->length());
1.21 for (unsigned i = 0; i < source->length(); i++)
1.22 @@ -295,26 +307,9 @@
1.23 bool
1.24 Array::ShallowCopy(Zone *zone, Handle<Array> dest, Handle<Array> source)
1.25 {
1.26 -#if 0
1.27 - Local<Type> contained(zone, source->type()->contained());
1.28 - if (contained->isArray() && ArrayType::cast(contained)->isFixedLength()) {
1.29 - assert(dest->type()->contained()->isArray());
1.30 - assert(source->length() == dest->length());
1.31 + Local<Type> contained(zone, dest->type()->contained());
1.32
1.33 - Local<Array> left(zone);
1.34 - Local<Array> right(zone);
1.35 - for (unsigned i = 0; i < source->length(); i++) {
1.36 - left = Array::cast(dest->readObject(i));
1.37 - right = Array::cast(source->readObject(i));
1.38 - if (!ShallowCopy(zone, left, right))
1.39 - return false;
1.40 - }
1.41 - return true;
1.42 - }
1.43 -
1.44 -#endif
1.45 - assert(false);
1.46 - return false;
1.47 + return CopyArrayElements(zone, contained, dest, source);
1.48 }
1.49
1.50 Array *
2.1 --- a/src/Interpreter.cpp Mon Dec 24 02:56:02 2012 -0800
2.2 +++ b/src/Interpreter.cpp Mon Dec 24 16:09:13 2012 -0800
2.3 @@ -82,29 +82,6 @@
2.4 return true;
2.5 }
2.6
2.7 -static bool
2.8 -DuplicateArray(Zone *zone, Handle<Array> src)
2.9 -{
2.10 - Local<ArrayMap> map(zone, ArrayMap::cast(src->map()));
2.11 - Local<Array> dest(zone);
2.12 -
2.13 - if (map->type()->isFixedLength()) {
2.14 - dest = Array::NewFixed(zone, map);
2.15 - } else {
2.16 - Type *type = map->type();
2.17 - int sizes[MAX_ARRAY_DEPTH];
2.18 - for (unsigned i = 0; i < map->type()->levels(); i++) {
2.19 - ArrayType *atype = ArrayType::cast(type);
2.20 - sizes[i] = atype->isFixedLength()
2.21 - ? atype->fixedLength()
2.22 - : 0;
2.23 - type = atype->contained();
2.24 - }
2.25 - }
2.26 -
2.27 - Local<Array> dst(zone);
2.28 -}
2.29 -
2.30 static void
2.31 GetElement(Array *array, int index, LocalSlot *vp)
2.32 {
3.1 --- a/src/Opcodes.tbl Mon Dec 24 02:56:02 2012 -0800
3.2 +++ b/src/Opcodes.tbl Mon Dec 24 16:09:13 2012 -0800
3.3 @@ -100,7 +100,8 @@
3.4 OPDEF(OP_NEWEMPTY, "newempty", 6, 0, 1)
3.5 OPDEF(OP_NEWSIZED, "newsized", 6, -1, 1)
3.6
3.7 -// Creates a duplicate copy of an array.
3.8 +// Creates a duplicate copy of an array. This is a deep clone, rather than
3.9 +// COPYARRAY, which is shallow.
3.10 OPDEF(OP_DUPARRAY, "duparray", 1, 1, 1)
3.11
3.12 // Performs an array-to-array assignment.
3.13 @@ -108,7 +109,7 @@
3.14 // Right = pop()
3.15 // copy(Left, Right)
3.16 // push(Left)
3.17 -OPDEF(OP_COPYARRAY, "arraycopy", 1, 2, 1)
3.18 +OPDEF(OP_COPYARRAY, "copyarray", 1, 2, 1)
3.19
3.20 // Reads or writes an integer element to an unsafe array.
3.21 // index = pop()
4.1 --- a/src/Types.cpp Mon Dec 24 02:56:02 2012 -0800
4.2 +++ b/src/Types.cpp Mon Dec 24 16:09:13 2012 -0800
4.3 @@ -135,86 +135,6 @@
4.4 return type;
4.5 }
4.6
4.7 -#if 0
4.8 -Type *
4.9 -Type::NewNative(Zone *zone, Handle<Type> returnType, Handle<FixedArray> parameters,
4.10 - Handle<FixedArray> defaults, bool variadic)
4.11 -{
4.12 - Local<Type> type(zone, Type::cast(zone->allocate(MapKind_Type, sizeof(Type), Heap::Tenure_Old)));
4.13 - if (!type)
4.14 - return NULL;
4.15 -
4.16 - type->kind_ = NATIVE;
4.17 - type->contained_ = NULL;
4.18 - type->fields_ = NULL;
4.19 - type->newMap_ = NULL;
4.20 - type->returnType_ = returnType;
4.21 - type->parameters_ = parameters;
4.22 - type->defaults_ = defaults;
4.23 - type->variadicNative_ = variadic;
4.24 - return type;
4.25 -}
4.26 -#endif
4.27 -
4.28 -bool
4.29 -Type::Compare(Type *left, Type *right)
4.30 -{
4.31 - if (left == right)
4.32 - return true;
4.33 -
4.34 - if (left->kind() != right->kind())
4.35 - return false;
4.36 -
4.37 - switch (left->kind()) {
4.38 - case Type::PRIMITIVE:
4.39 - return left->primitive() == right->primitive();
4.40 -
4.41 - case Type::ARRAY:
4.42 - {
4.43 - ArrayType *aleft = ArrayType::cast(left);
4.44 - ArrayType *aright = ArrayType::cast(right);
4.45 - if (aleft->levels() != aright->levels())
4.46 - return false;
4.47 - if (aleft->isFixedLength() != aright->isFixedLength())
4.48 - return false;
4.49 - if (aleft->isFixedLength() && aleft->fixedLength() != aright->fixedLength())
4.50 - return false;
4.51 - return Compare(aleft->contained(), aright->contained());
4.52 - }
4.53 -
4.54 - case Type::FUNCTION:
4.55 - {
4.56 - FunctionType *fleft = FunctionType::cast(left);
4.57 - FunctionType *fright = FunctionType::cast(right);
4.58 - if (!Compare(fleft->returnType(), fright->returnType()))
4.59 - return false;
4.60 - if (fleft->parameters()->length() != fright->parameters()->length())
4.61 - return false;
4.62 - if (fleft->isNative() != fright->isNative())
4.63 - return false;
4.64 - if (fleft->isNative() && (fleft->isNativeVariadic() != fright->isNativeVariadic()))
4.65 - return false;
4.66 - for (unsigned i = 0; i < fleft->parameters()->length(); i++) {
4.67 - Type *leftparam = fleft->parameterAt(i);
4.68 - Type *rightparam = fright->parameterAt(i);
4.69 - if (!Compare(leftparam, rightparam))
4.70 - return false;
4.71 - }
4.72 - return true;
4.73 - }
4.74 -
4.75 - case Type::ENUM:
4.76 - return false;
4.77 -
4.78 - case Type::VOID:
4.79 - return true;
4.80 -
4.81 - default:
4.82 - assert(left->kind() == Type::REFERENCE);
4.83 - return Compare(ReferenceType::cast(left)->contained(), ReferenceType::cast(right)->contained());
4.84 - }
4.85 -}
4.86 -
4.87 StructType *
4.88 StructType::New(Zone *zone, Handle<String> name, Handle<FixedArray> fields)
4.89 {
5.1 --- a/src/Types.h Mon Dec 24 02:56:02 2012 -0800
5.2 +++ b/src/Types.h Mon Dec 24 16:09:13 2012 -0800
5.3 @@ -113,8 +113,6 @@
5.4 static Type *NewPrimitive(Zone *zone, PrimitiveType type);
5.5 static Type *NewString(Zone *zone);
5.6
5.7 - static bool Compare(Type *left, Type *right);
5.8 -
5.9 static Type *cast(Object *obj) {
5.10 assert(!obj ||
5.11 obj->is(MapKind_Type) ||
6.1 --- a/src/compiler/HIR.cpp Mon Dec 24 02:56:02 2012 -0800
6.2 +++ b/src/compiler/HIR.cpp Mon Dec 24 16:09:13 2012 -0800
6.3 @@ -75,11 +75,8 @@
6.4 {
6.5 __ object(hir->array());
6.6
6.7 - if (!hir->type()->isConst()) {
6.8 - // If the type isn't const, we have to assume a consumer may modify
6.9 - // the array, and therefore we have to duplicate it here.
6.10 + if (hir->duplicate())
6.11 __ duparray();
6.12 - }
6.13 }
6.14
6.15 void
6.16 @@ -355,16 +352,30 @@
6.17 HIRTranslator::visitStore(HStore *store)
6.18 {
6.19 store->lval()->bind(this, emitter_);
6.20 +
6.21 if (store->kind() == TOK_ASSIGN) {
6.22 - store->rval()->accept(this);
6.23 + // Fixed-length arrays and structs have copy semantics.
6.24 + Local<Type> type(zone_, store->lval()->type());
6.25 + if (type->isArray() && ArrayType::cast(type)->isFixedLength()) {
6.26 + store->lval()->load(emitter_);
6.27 + store->rval()->accept(this);
6.28 + __ copyarray();
6.29 + } else if (type->isStruct()) {
6.30 + store->lval()->load(emitter_);
6.31 + store->rval()->accept(this);
6.32 + __ copystruct();
6.33 + } else {
6.34 + store->rval()->accept(this);
6.35 + store->lval()->store(emitter_);
6.36 + }
6.37 } else {
6.38 // Advanced case: dup the ref, load, op, store.
6.39 store->lval()->hold(emitter_);
6.40 store->lval()->load(emitter_);
6.41 store->rval()->accept(this);
6.42 __ binary(TokenToOpcode(store->kind()));
6.43 + store->lval()->store(emitter_);
6.44 }
6.45 - store->lval()->store(emitter_);
6.46 }
6.47
6.48 void
7.1 --- a/src/compiler/HIR.h Mon Dec 24 02:56:02 2012 -0800
7.2 +++ b/src/compiler/HIR.h Mon Dec 24 16:09:13 2012 -0800
7.3 @@ -166,9 +166,10 @@
7.4 class HArray : public HIR
7.5 {
7.6 public:
7.7 - HArray(AstNode *node, Type *type, Array *array)
7.8 + HArray(AstNode *node, Type *type, Array *array, bool duplicate)
7.9 : HIR(node, type),
7.10 - array_(array)
7.11 + array_(array),
7.12 + duplicate_(duplicate)
7.13 {
7.14 }
7.15
7.16 @@ -176,9 +177,13 @@
7.17 Handle<Array> array() const {
7.18 return array_;
7.19 }
7.20 + bool duplicate() const {
7.21 + return duplicate_;
7.22 + }
7.23
7.24 private:
7.25 ScopedRoot<Array> array_;
7.26 + bool duplicate_;
7.27 };
7.28
7.29 class HConvert : public HIR
8.1 --- a/src/compiler/SemanticAnalysis.cpp Mon Dec 24 02:56:02 2012 -0800
8.2 +++ b/src/compiler/SemanticAnalysis.cpp Mon Dec 24 16:09:13 2012 -0800
8.3 @@ -436,7 +436,7 @@
8.4
8.5 if ((hir = eval(node->initialization())) == NULL)
8.6 return;
8.7 - if ((hir = coerce(hir, type, Coerce_Assign)) == NULL)
8.8 + if ((hir = coerce(hir, type, Coerce_Decl)) == NULL)
8.9 return;
8.10
8.11 discharge(hir);
8.12 @@ -455,8 +455,23 @@
8.13
8.14 if (atype->isFixedLength()) {
8.15 __ newarray(OP_NEWFIXED, map, InferTenure(node->sym()));
8.16 - } else if (node->dims()) {
8.17 - assert(false);
8.18 + } else if (node->dims()->at(0)) {
8.19 + // This may not be true if we allow typedefing, so :TODO:
8.20 + // figure out proper semantics.
8.21 + assert(node->dims()->length() == atype->levels());
8.22 +
8.23 + Local<Type> i32(zone_, zone_->types()->getPrimitive(PrimitiveType_Int32));
8.24 + for (size_t i = 0; i < node->dims()->length(); i++) {
8.25 + Expression *expr = node->dims()->at(i);
8.26 + if (expr) {
8.27 + HIR *hir = eval(expr);
8.28 + if (!hir || ((hir = coerce(hir, i32, Coerce_Arg)) == NULL))
8.29 + return;
8.30 + discharge(hir);
8.31 + } else {
8.32 + __ int_(0);
8.33 + }
8.34 + }
8.35 __ newarray(OP_NEWSIZED, map, InferTenure(node->sym()));
8.36 } else {
8.37 __ newarray(OP_NEWEMPTY, map, InferTenure(node->sym()));
8.38 @@ -833,9 +848,7 @@
8.39 HString *hir = from->toString();
8.40 Local<ArrayType> actual(zone_, ArrayType::cast(argActual));
8.41
8.42 - // If assigning or returning, and the desired type is fixed length,
8.43 - // make sure there is enough room.
8.44 - if ((kind == Coerce_Return || kind == Coerce_Assign) && actual->isFixedLength()) {
8.45 + if (actual->isFixedLength()) {
8.46 if (hir->string()->length() + 1 > actual->fixedLength()) {
8.47 cc_.reportError(pos, Message_ArrayAssignmentTooBig);
8.48 return NULL;
8.49 @@ -858,7 +871,25 @@
8.50 if (!array)
8.51 return NULL;
8.52
8.53 - return new (pool_) HArray(hir->node(), actual, array);
8.54 + // Since we're creating a canonical copy of the char array, we have to
8.55 + // be careful that it doesn't get mutated. There are two cases where
8.56 + // we know it can't be mutated, and therefore don't need to specify
8.57 + // that it be cloned after pushing it onto the stack.
8.58 + //
8.59 + // (1) The array type is const.
8.60 + // (2) The array type is fixed-length, and the coercion is for
8.61 + // assignment, meaning it will be immediately copied.
8.62 + //
8.63 + // We treat declaration separately from assignment, since for
8.64 + // declaration we can just immediately use the cloned version.
8.65 + bool shouldDuplicate = true;
8.66 + if (actual->isConst() ||
8.67 + (actual->isFixedLength() && kind == Coerce_Assign))
8.68 + {
8.69 + shouldDuplicate = false;
8.70 + }
8.71 +
8.72 + return new (pool_) HArray(hir->node(), actual, array, shouldDuplicate);
8.73 }
8.74
8.75 if (!argGiven->isArray()) {
8.76 @@ -877,23 +908,21 @@
8.77 unsigned level = 1;
8.78 while (true) {
8.79 if (actual->isFixedLength()) {
8.80 - if (kind == Coerce_Return || kind == Coerce_Assign) {
8.81 - if (!given->isFixedLength()) {
8.82 - cc_.reportError(pos, Message_ArrayDimensionSizeMismatch, level);
8.83 + if (!given->isFixedLength()) {
8.84 + cc_.reportError(pos, Message_ArrayDimensionSizeMismatch, level);
8.85 + return NULL;
8.86 + }
8.87 +
8.88 + if (actual->isCharArray()) {
8.89 + // String assignment can have a truncated |given|.
8.90 + if (given->fixedLength() > actual->fixedLength()) {
8.91 + cc_.reportError(pos, Message_ArrayAssignmentTooBig);
8.92 return NULL;
8.93 }
8.94 -
8.95 - if (actual->isCharArray()) {
8.96 - // String assignment can have a truncated |given|.
8.97 - if (given->fixedLength() > actual->fixedLength()) {
8.98 - cc_.reportError(pos, Message_ArrayAssignmentTooBig);
8.99 - return NULL;
8.100 - }
8.101 - } else {
8.102 - if (given->fixedLength() != actual->fixedLength()) {
8.103 - cc_.reportError(pos, Message_ArrayDimensionSizeMismatch, level);
8.104 - return NULL;
8.105 - }
8.106 + } else {
8.107 + if (given->fixedLength() != actual->fixedLength()) {
8.108 + cc_.reportError(pos, Message_ArrayDimensionSizeMismatch, level);
8.109 + return NULL;
8.110 }
8.111 }
8.112 } else if (given->isFixedLength()) {
9.1 --- a/src/compiler/SemanticAnalysis.h Mon Dec 24 02:56:02 2012 -0800
9.2 +++ b/src/compiler/SemanticAnalysis.h Mon Dec 24 16:09:13 2012 -0800
9.3 @@ -85,6 +85,7 @@
9.4 enum CoercionKind {
9.5 Coerce_Arg,
9.6 Coerce_Return,
9.7 + Coerce_Decl,
9.8 Coerce_Assign
9.9 };
9.10
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
10.2 +++ b/tests/basic/string-lit-2.out Mon Dec 24 16:09:13 2012 -0800
10.3 @@ -0,0 +1,1 @@
10.4 +6568101121
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
11.2 +++ b/tests/basic/string-lit-2.sp Mon Dec 24 16:09:13 2012 -0800
11.3 @@ -0,0 +1,26 @@
11.4 +native PrintNum(num);
11.5 +
11.6 +f(x)
11.7 +{
11.8 + new char:str1[];
11.9 + new char:str2[4];
11.10 +
11.11 + str1 = "hello";
11.12 + str2 = "bye";
11.13 +
11.14 + // Make sure modifying the string doesn't modify the
11.15 + // canonical literals.
11.16 + if (x) {
11.17 + str1[1] = 65;
11.18 + str2[1] = 68;
11.19 + }
11.20 +
11.21 + PrintNum(str1[1]);
11.22 + PrintNum(str2[1]);
11.23 +}
11.24 +
11.25 +public main()
11.26 +{
11.27 + f(1);
11.28 + f(0);
11.29 +}