Beispiel #1
0
TCResult ast::SliceOp::typecheck(sst::TypecheckState* fs, fir::Type* inferred)
{
	fs->pushLoc(this);
	defer(fs->popLoc());

	fs->pushAnonymousTree();
	defer(fs->popTree());

	auto array = this->expr->typecheck(fs).expr();
	auto ty = array->type;

	fs->enterSubscript(array);
	defer(fs->leaveSubscript());

	fir::Type* elm = 0;
	if(ty->isDynamicArrayType() || ty->isArraySliceType() || ty->isArrayType())
		elm = ty->getArrayElementType();

	else if(ty->isStringType())
		elm = fir::Type::getInt8();

	else if(ty->isPointerType())
		elm = ty->getPointerElementType();

	else
		error(array, "invalid type '%s' for slice operation", ty);

	auto begin = this->start ? this->start->typecheck(fs, fir::Type::getInt64()).expr() : 0;
	auto end = this->end ? this->end->typecheck(fs, fir::Type::getInt64()).expr() : 0;

	if(begin && !begin->type->isIntegerType())
		error(begin, "expected integer type for start index of slice; found '%s'", begin->type);

	if(end && !end->type->isIntegerType())
		error(end, "expected integer type for end index of slice; found '%s'", end->type);

	//* how it goes:
	// 1. strings and dynamic arrays are always sliced mutably.
	// 2. slices of slices naturally inherit their mutability.
	// 3. arrays are sliced immutably.
	// 4. pointers inherit their mutability as well.

	bool ismut = sst::getMutabilityOfSliceOfType(ty);

	auto ret = util::pool<sst::SliceOp>(this->loc, fir::ArraySliceType::get(elm, ismut));
	ret->expr = array;
	ret->begin = begin;
	ret->end = end;

	return TCResult(ret);
}
Parameter Parameter::getElementParameter()
{
  Parameter element_parameter;
  element_parameter.setType(getArrayElementType());
  element_parameter.setUnits(getUnits());
  if (rangeIsSet())
  {
    element_parameter.setRange(getRangeMin(),getRangeMax());
  }
  if (subsetIsSet())
  {
    element_parameter.setSubset(getSubset());
  }
  return element_parameter;
}
Beispiel #3
0
static void checkArray(cgn::CodegenState* cs, const DecompMapping& bind, CGResult rhs)
{
	iceAssert(bind.array);

	auto rt = rhs.value->getType();
	bool shouldSliceBeMutable = sst::getMutabilityOfSliceOfType(rt);

	if(!rt->isArrayType() && !rt->isDynamicArrayType() && !rt->isArraySliceType() && !rt->isStringType())
		error(bind.loc, "Expected array type in destructuring declaration; found type '%s' instead", rt);

	if(rt->isStringType())
	{
		// do a bounds check.
		auto numbinds = fir::ConstantInt::getInt64(bind.inner.size());
		{
			auto checkf = cgn::glue::string::getBoundsCheckFunction(cs, true);
			iceAssert(checkf);

			auto strloc = fir::ConstantString::get(bind.loc.toString());
			cs->irb.Call(checkf, cs->irb.GetSAALength(rhs.value), numbinds, strloc);
		}

		//* note: special-case this, because 1. we want to return chars
		auto strdat = cs->irb.PointerTypeCast(cs->irb.GetSAAData(rhs.value), fir::Type::getMutInt8Ptr());
		{
			size_t idx = 0;
			for(auto& b : bind.inner)
			{
				auto v = CGResult(cs->irb.ReadPtr(cs->irb.PointerAdd(strdat, fir::ConstantInt::getInt64(idx))));
				cs->generateDecompositionBindings(b, v, false);

				idx++;
			}
		}

		if(!bind.restName.empty())
		{
			if(bind.restRef)
			{
				// make a slice of char.
				auto remaining = cs->irb.Subtract(cs->irb.GetSAALength(rhs.value), numbinds);

				auto slice = cs->irb.CreateValue(fir::Type::getCharSlice(shouldSliceBeMutable));
				slice = cs->irb.SetArraySliceData(slice, cs->irb.PointerAdd(strdat, numbinds));
				slice = cs->irb.SetArraySliceLength(slice, remaining);

				handleDefn(cs, bind.restDefn, CGResult(slice));
			}
			else
			{
				// make string.
				// auto remaining = cs->irb.Subtract(cs->irb.GetSAALength(rhs.value), numbinds);

				auto clonef = cgn::glue::string::getCloneFunction(cs);
				iceAssert(clonef);

				auto string = cs->irb.Call(clonef, rhs.value, numbinds);

				handleDefn(cs, bind.restDefn, CGResult(string));
			}
		}
	}
	else
	{
		auto array = rhs.value;
		fir::Value* arrlen = 0;

		auto numbinds = fir::ConstantInt::getInt64(bind.inner.size());
		{
			//* note: 'true' means we're performing a decomposition, so print a more appropriate error message on bounds failure.
			auto checkf = cgn::glue::array::getBoundsCheckFunction(cs, true);
			iceAssert(checkf);

			if(rt->isArrayType())               arrlen = fir::ConstantInt::getInt64(rt->toArrayType()->getArraySize());
			else if(rt->isArraySliceType())     arrlen = cs->irb.GetArraySliceLength(array);
			else if(rt->isDynamicArrayType())   arrlen = cs->irb.GetSAALength(array);
			else                                iceAssert(0);

			auto strloc = fir::ConstantString::get(bind.loc.toString());
			cs->irb.Call(checkf, arrlen, numbinds, strloc);
		}

		// # if 0
		if(!rhs->islorclvalue() && rt->isArrayType())
		{
			//* because of the way LLVM is designed, and hence by extension how we are designed,
			//* fixed-sized arrays are kinda dumb. If we don't have a pointer to the array (for whatever reason???),
			//* then we can't do a GEP access, and hence can't get a pointer to use for the 'rest' binding. So,
			//* we error on that case but allow binding the rest.

			//* theoretically if the compiler is well designed we should never hit this case, but who knows?

			size_t idx = 0;
			for(auto& b : bind.inner)
			{
				auto v = CGResult(cs->irb.ExtractValue(array, { idx }));
				cs->generateDecompositionBindings(b, v, false);

				idx++;
			}

			warn(bind.loc, "Destructure of array without pointer (shouldn't happen!)");
			if(!bind.restName.empty())
				error(bind.loc, "Could not get pointer to array (of type '%s') to create binding for '...'", rt);
		}
		else
		// #endif

		{
			fir::Value* data = 0;

			if(rt->isArrayType())               data = cs->irb.ConstGEP2(rhs.value, 0, 0);
			else if(rt->isArraySliceType())     data = cs->irb.GetArraySliceData(array);
			else if(rt->isDynamicArrayType())   data = cs->irb.GetSAAData(array);
			else                                iceAssert(0);


			size_t idx = 0;
			for(auto& b : bind.inner)
			{
				auto ptr = cs->irb.PointerAdd(data, fir::ConstantInt::getInt64(idx));

				auto v = CGResult(cs->irb.Dereference(ptr));
				cs->generateDecompositionBindings(b, v, true);

				idx++;
			}

			if(!bind.restName.empty())
			{
				if(bind.restRef)
				{
					auto sty = fir::ArraySliceType::get(rt->getArrayElementType(), shouldSliceBeMutable);

					auto remaining = cs->irb.Subtract(arrlen, numbinds);

					auto slice = cs->irb.CreateValue(sty);
					slice = cs->irb.SetArraySliceData(slice, cs->irb.PointerAdd(data, numbinds));
					slice = cs->irb.SetArraySliceLength(slice, remaining);

					handleDefn(cs, bind.restDefn, CGResult(slice));
				}
				else
				{
					// always return a dynamic array here.
					//* note: in order to make our lives somewhat easier, for fixed arrays, we create a fake slice pointing to its data, then we
					//* call clone on that instead.

					fir::Value* clonee = 0;
					if(rt->isArrayType())
					{
						clonee = cs->irb.CreateValue(fir::ArraySliceType::get(rt->getArrayElementType(), shouldSliceBeMutable));
						clonee = cs->irb.SetArraySliceData(clonee, data);
						clonee = cs->irb.SetArraySliceLength(clonee, fir::ConstantInt::getInt64(rt->toArrayType()->getArraySize()));
					}
					else
					{
						clonee = array;
					}

					auto clonef = cgn::glue::array::getCloneFunction(cs, clonee->getType());
					iceAssert(clonef);

					auto ret = cs->irb.Call(clonef, clonee, numbinds);

					handleDefn(cs, bind.restDefn, CGResult(ret));
				}
			}
		}
	}
}
void Parameter::writeApi(Response & response,
  bool write_name_only,
  bool is_property,
  bool write_firmware,
  bool write_instance_details)
{
  if (response.error())
  {
    return;
  }

  const ConstantString & name = getName();
  if (write_name_only)
  {
    response.write(name);
    return;
  }

  response.beginObject();

  response.write(constants::name_constant_string,name);

  if (write_firmware)
  {
    const ConstantString & firmware_name = getFirmwareName();
    response.write(constants::firmware_constant_string,firmware_name);
  }

  JsonStream::JsonTypes type = getType();
  if (write_instance_details)
  {
    switch (type)
    {
      case JsonStream::LONG_TYPE:
      {
        response.write(constants::type_constant_string,JsonStream::LONG_TYPE);
        if (subsetIsSet())
        {
          response.writeKey(constants::subset_constant_string);
          response.write(getSubset(),JsonStream::LONG_TYPE);
        }
        if (rangeIsSet())
        {
          long min = getRangeMin().l;
          long max = getRangeMax().l;
          response.write(constants::min_constant_string,min);
          response.write(constants::max_constant_string,max);
        }
        break;
      }
      case JsonStream::DOUBLE_TYPE:
      {
        response.write(constants::type_constant_string,JsonStream::DOUBLE_TYPE);
        if (rangeIsSet())
        {
          double min = getRangeMin().d;
          double max = getRangeMax().d;
          response.write(constants::min_constant_string,min);
          response.write(constants::max_constant_string,max);
        }
        break;
      }
      case JsonStream::BOOL_TYPE:
      {
        response.write(constants::type_constant_string,JsonStream::BOOL_TYPE);
        break;
      }
      case JsonStream::NULL_TYPE:
      {
        break;
      }
      case JsonStream::STRING_TYPE:
      {
        response.write(constants::type_constant_string,JsonStream::STRING_TYPE);
        if (subsetIsSet())
        {
          response.writeKey(constants::subset_constant_string);
          response.write(getSubset(),JsonStream::STRING_TYPE);
        }
        break;
      }
      case JsonStream::OBJECT_TYPE:
      {
        response.write(constants::type_constant_string,JsonStream::OBJECT_TYPE);
        break;
      }
      case JsonStream::ARRAY_TYPE:
      {
        response.write(constants::type_constant_string,JsonStream::ARRAY_TYPE);
        JsonStream::JsonTypes array_element_type = getArrayElementType();
        switch (array_element_type)
        {
          case JsonStream::LONG_TYPE:
          {
            response.write(constants::array_element_type_constant_string,JsonStream::LONG_TYPE);
            if (subsetIsSet())
            {
              response.writeKey(constants::array_element_subset_constant_string);
              response.write(getSubset(),JsonStream::LONG_TYPE);
            }
            if (rangeIsSet())
            {
              long min = getRangeMin().l;
              long max = getRangeMax().l;
              response.write(constants::array_element_min_constant_string,min);
              response.write(constants::array_element_max_constant_string,max);
            }
            break;
          }
          case JsonStream::DOUBLE_TYPE:
          {
            response.write(constants::array_element_type_constant_string,JsonStream::DOUBLE_TYPE);
            if (rangeIsSet())
            {
              double min = getRangeMin().d;
              double max = getRangeMax().d;
              response.write(constants::array_element_min_constant_string,min);
              response.write(constants::array_element_max_constant_string,max);
            }
            break;
          }
          case JsonStream::BOOL_TYPE:
          {
            response.write(constants::array_element_type_constant_string,JsonStream::BOOL_TYPE);
            break;
          }
          case JsonStream::NULL_TYPE:
          {
            break;
          }
          case JsonStream::STRING_TYPE:
          {
            response.write(constants::array_element_type_constant_string,JsonStream::STRING_TYPE);
            if (subsetIsSet())
            {
              response.writeKey(constants::array_element_subset_constant_string);
              response.write(getSubset(),JsonStream::STRING_TYPE);
            }
            break;
          }
          case JsonStream::OBJECT_TYPE:
          {
            response.write(constants::array_element_type_constant_string,JsonStream::OBJECT_TYPE);
            break;
          }
          case JsonStream::ARRAY_TYPE:
          {
            response.write(constants::array_element_type_constant_string,JsonStream::ARRAY_TYPE);
            break;
          }
          case JsonStream::ANY_TYPE:
          {
            response.write(constants::array_element_type_constant_string,JsonStream::ANY_TYPE);
            break;
          }
        }
        if (arrayLengthRangeIsSet() && !is_property)
        {
          size_t array_length_min = getArrayLengthMin();
          size_t array_length_max = getArrayLengthMax();
          response.write(constants::array_length_min_constant_string,array_length_min);
          response.write(constants::array_length_max_constant_string,array_length_max);
        }
        break;
      }
      case JsonStream::ANY_TYPE:
      {
        response.write(constants::type_constant_string,JsonStream::ANY_TYPE);
        break;
      }
    }
  }
  else
  {
    response.write(constants::type_constant_string,type);
    if (type == JsonStream::ARRAY_TYPE)
    {
      JsonStream::JsonTypes array_element_type = getArrayElementType();
      response.write(constants::array_element_type_constant_string,array_element_type);
    }
  }

  if (write_instance_details)
  {
    const ConstantString & units = getUnits();
    if (units.length() != 0)
    {
      response.write(constants::units_constant_string,units);
    }
  }

  if (!is_property)
  {
    response.endObject();
  }
}
IHqlExpression * HqlCppCaseInfo::buildIndexedMap(BuildCtx & ctx, const CHqlBoundExpr & test)
{
    ITypeInfo * compareType = test.queryType()->queryPromotedType();
    type_t compareTypeCode = compareType->getTypeCode();

    HqlExprArray values;
    IHqlExpression * dft = queryActiveTableSelector();  // value doesn't matter as long as it will not occur
    __int64 lower = getIntValue(lowerTableBound, 0);
    unsigned num = (getIntValue(upperTableBound, 0)-lower)+1;

    CHqlBoundExpr indexExpr;
    switch (compareTypeCode)
    {
        case type_int:
            indexExpr.set(test);
            break;
        case type_string:
            indexExpr.expr.setown(createValue(no_index, makeCharType(), LINK(test.expr), getZero()));
            indexExpr.expr.setown(createValue(no_cast, makeIntType(1, false), LINK(indexExpr.expr)));
            break;
        default:
            throwUnexpectedType(compareType);
    }

    if (useRangeIndex && (num != 1))
        translator.ensureSimpleExpr(ctx, indexExpr);

    OwnedHqlExpr mapped;
    ITypeInfo * retType = resultType;
    //if num == pairs.ordinality() and all results are identical, avoid the table lookup.
    if (allResultsMatch && (num == pairs.ordinality()))
    {
        mapped.set(pairs.item(0).queryChild(1));
    }
    else
    {
        values.ensure(num);
        unsigned idx;
        for (idx = 0; idx < num; idx++)
            values.append(*LINK(dft));

        ForEachItemIn(idx2, pairs)
        {
            IHqlExpression & cur = pairs.item(idx2);
            IValue * value = cur.queryChild(0)->queryValue();
            unsigned replaceIndex;
            switch (compareTypeCode)
            {
            case type_int:
                replaceIndex = (unsigned)(value->getIntValue()-lower);
                break;
            case type_string:
                {
                    StringBuffer temp;
                    value->getStringValue(temp);
                    replaceIndex = (unsigned)((unsigned char)temp.charAt(0)-lower);
                    break;
                }
            default:
                throwUnexpectedType(compareType);
            }

            IHqlExpression * mapTo = cur.queryChild(1);
            if (mapTo->getOperator() != no_constant)
                throwUnexpected();
            if (replaceIndex >= num)
                translator.reportWarning(CategoryIgnored, HQLWRN_CaseCanNeverMatch, "CASE entry %d can never match the test condition", replaceIndex);
            else
                values.replace(*LINK(mapTo),replaceIndex);
        }

        //Now replace the placeholders with the default values.
        for (idx = 0; idx < num; idx++)
        {
            if (&values.item(idx) == dft)
                values.replace(*defaultValue.getLink(),idx);
        }

        // use a var string type to get better C++ generated...
        ITypeInfo * storeType = getArrayElementType(resultType);
        ITypeInfo * listType = makeArrayType(storeType, values.ordinality());
        OwnedHqlExpr lvalues = createValue(no_list, listType, values);

        CHqlBoundExpr boundTable;
        translator.buildExpr(ctx, lvalues, boundTable);

        LinkedHqlExpr tableIndex = indexExpr.expr;
        if (getIntValue(lowerTableBound, 0))
            tableIndex.setown(createValue(no_sub, tableIndex->getType(), LINK(tableIndex), LINK(lowerTableBound)));

        IHqlExpression * ret = createValue(no_index, LINK(retType), LINK(boundTable.expr), LINK(tableIndex));
        mapped.setown(createTranslatedOwned(ret));
    }
Beispiel #6
0
CGResult sst::AssignOp::_codegen(cgn::CodegenState* cs, fir::Type* infer)
{
	cs->pushLoc(this);
	defer(cs->popLoc());

	auto lr = this->left->codegen(cs).value;
	auto lt = lr->getType();

	if(!lr->islorclvalue())
	{
		SpanError::make(SimpleError::make(this->loc, "Cannot assign to non-lvalue (most likely a temporary) expression"))
			->add(util::ESpan(this->left->loc, "here"))
			->postAndQuit();
	}
	else if(lr->isclvalue())
	{
		SpanError::make(SimpleError::make(this->loc, "Cannot assign to immutable expression"))
			->add(util::ESpan(this->left->loc, "here"))
			->postAndQuit();
	}


	// okay, i guess
	auto rr = this->right->codegen(cs, lt).value;
	auto rt = rr->getType();

	if(this->op != Operator::Assign)
	{
		// ok it's a compound assignment
		// auto [ newl, newr ] = cs->autoCastValueTypes(lr, rr);
		auto nonass = Operator::getNonAssignmentVersion(this->op);

		// some things -- if we're doing +=, and the types are supported, then just call the actual
		// append function, instead of doing the + first then assigning it.

		if(nonass == Operator::Plus)
		{
			if(lt->isDynamicArrayType() && lt == rt)
			{
				// right then.
				if(!lr->islvalue())
					error(this, "Cannot append to an r-value array");

				auto appendf = cgn::glue::array::getAppendFunction(cs, lt->toDynamicArrayType());

				//? are there any ramifications for these actions for ref-counted things?
				auto res = cs->irb.Call(appendf, lr, cs->irb.CreateSliceFromSAA(rr, false));

				cs->irb.Store(res, lr);
				return CGResult(0);
			}
			else if(lt->isDynamicArrayType() && lt->getArrayElementType() == rt)
			{
				// right then.
				if(!lr->islvalue())
					error(this, "Cannot append to an r-value array");

				auto appendf = cgn::glue::array::getElementAppendFunction(cs, lt->toDynamicArrayType());

				//? are there any ramifications for these actions for ref-counted things?
				auto res = cs->irb.Call(appendf, lr, rr);

				cs->irb.Store(res, lr);
				return CGResult(0);
			}
			else if(lt->isStringType() && lt == rt)
			{
				// right then.
				if(!lr->islvalue())
					error(this, "Cannot append to an r-value array");

				auto appendf = cgn::glue::string::getAppendFunction(cs);

				//? are there any ramifications for these actions for ref-counted things?
				auto res = cs->irb.Call(appendf, lr, cs->irb.CreateSliceFromSAA(rr, true));

				cs->irb.Store(res, lr);
				return CGResult(0);
			}
			else if(lt->isStringType() && rt->isCharType())
			{
				// right then.
				if(!lr->islvalue())
					error(this, "Cannot append to an r-value string");

				auto appendf = cgn::glue::string::getCharAppendFunction(cs);

				//? are there any ramifications for these actions for ref-counted things?
				auto res = cs->irb.Call(appendf, lr, rr);

				cs->irb.Store(res, lr);
				return CGResult(0);
			}
		}


		// do the op first
		auto res = cs->performBinaryOperation(this->loc, { this->left->loc, lr }, { this->right->loc, rr }, nonass);

		// assign the res to the thing
		rr = res.value;
	}

	rr = cs->oneWayAutocast(rr, lt);

	if(rr == 0)
	{
		error(this, "Invalid assignment from value of type '%s' to expected type '%s'", rr->getType(), lt);
	}

	// ok then
	if(lt != rr->getType())
		error(this, "What? left = %s, right = %s", lt, rr->getType());

	cs->autoAssignRefCountedValue(lr, rr, /* isInitial: */ false, /* performStore: */ true);
	return CGResult(0);
}