Re-implement array assignment.
authorDavid Anderson <dvander@alliedmods.net>
Mon Dec 24 16:09:13 2012 -0800 (2012-12-24)
changeset 236072f1114ea5d
parent 235 43287eef2fff
child 237 c77540a61905
Re-implement array assignment.
src/Array.cpp
src/Interpreter.cpp
src/Opcodes.tbl
src/Types.cpp
src/Types.h
src/compiler/HIR.cpp
src/compiler/HIR.h
src/compiler/SemanticAnalysis.cpp
src/compiler/SemanticAnalysis.h
tests/basic/string-lit-2.out
tests/basic/string-lit-2.sp
     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 +}