Do not document enum_minbits, enum_issigned, enum_mintype upon a third thought.

/*
 * Copyright 2004-2015 Cray Inc.
 * Other additional copyright holders may be indicated within.
 *
 * The entirety of this work is licensed under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License.
 *
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
Functions related to predefined types.

This is an automatic module, so these functions are available
to all Chapel programs.
*/
module Types {

pragma "no doc" // joint documentation with the next one
pragma "no instantiation limit"
proc isType(type t) param return true;
/* Returns `true` if the argument is a type. */
pragma "no instantiation limit"
proc isType(e) param return false;

pragma "no doc" // joint documentation with the next one
pragma "no instantiation limit"
proc isParam(type t)  param return false;
pragma "no doc" // joint documentation with the next one
pragma "no instantiation limit"
proc isParam(param p) param return true;
/* Returns `true` if the argument is a param. */
pragma "no instantiation limit"
proc isParam(e)       param return false;

// TODO eliminate this; beware of isPrimitive()
pragma "no doc"
pragma "no instantiation limit"
proc _isPrimitiveType(type t) param return
  isBoolType(t)  ||
  isIntegralType(t) ||
  isRealType(t)     ||
//To allow imag, need to define casts from primitive types into imag.
//isImagType(t)     ||
  (t == c_string);

/*
Returns `true` if the type `t` is a primitive type,
as defined by the language specification.
*/
pragma "no instantiation limit"
proc isPrimitiveType(type t) param return
  isVoidType(t) || isBoolType(t) || isIntType(t) || isUintType(t) ||
  isRealType(t) || isImagType(t) || isComplexType(t) || isStringType(t);

/*
Returns `true` if the type `t` is one the following types, of any width:
`int`, `uint`, `real`, `imag`, `complex`.
*/
pragma "no instantiation limit"
proc isNumericType(type t) param return
  isIntegralType(t) || isFloatType(t) || isComplexType(t);

/*
Returns `true` if the type `t` is one the following types, of any width:
`int`, `uint`.
*/
pragma "no instantiation limit"
proc isIntegralType(type t) param return
  isIntType(t) || isUintType(t);

/*
Returns `true` if the type `t` is one the following types, of any width:
`real`, `imag`.
*/
pragma "no instantiation limit"
proc isFloatType(type t) param return
  isRealType(t) || isImagType(t);

// We document the following functions as one group, for user convenience.
pragma "no instantiation limit"
proc isVoidType(type t) param return t == void;

pragma "no instantiation limit"
proc isBoolType(type t) param return
  (t == bool) | (t == bool(8)) | (t == bool(16)) | (t == bool(32)) | (t == bool(64));

pragma "no instantiation limit"
proc isIntType(type t) param return
  (t == int(8)) || (t == int(16)) || (t == int(32)) || (t == int(64));

pragma "no instantiation limit"
proc isUintType(type t) param return
  (t == uint(8)) || (t == uint(16)) || (t == uint(32)) || (t == uint(64));

pragma "no instantiation limit"
proc isEnumType(type t) param {
  proc isEnumHelp(type t: enumerated) param return true;
  proc isEnumHelp(type t) param return false;
  return isEnumHelp(t);
}

pragma "no instantiation limit"
proc isComplexType(type t) param return
  (t == complex(64)) | (t == complex(128));

pragma "no instantiation limit"
proc isRealType(type t) param return
  (t == real(32)) | (t == real(64));

pragma "no instantiation limit"
proc isImagType(type t) param return
  (t == imag(32)) | (t == imag(64));

// Here is a single doc comment for the above.
/*
Each of the above functions returns `true` if its argument is
a corresponding type.
The argument must be a type.
*/
pragma "no instantiation limit"
proc isStringType(type t) param return t == string;

// Returns the unsigned equivalent of the input type.
pragma "no doc"
proc chpl__unsignedType(type t) type
{
  if ! isIntegralType(t) then
    compilerError("range idxType is non-integral: ", typeToString(t));

  return uint(numBits(t));
}

// Returns the signed equivalent of the input type.
pragma "no doc"
proc chpl__signedType(type t) type
{
  if ! isIntegralType(t) then
    compilerError("range idxType is non-integral: ", typeToString(t));

  return int(numBits(t));
}

pragma "no doc"
proc chpl__maxIntTypeSameSign(type t) type {
  if ! isIntegralType(t) then
    compilerError("type t is non-integral: ", typeToString(t));

  if (isIntType(t)) then
    return int(64);
  else
    return uint(64);
}

//
// isXxxValue() - trivial implementations
// no isVoid() or isVoidValue() - it might not work
// Let these be for internal consumption for now,
// due to lack of consensus for the name.
//

pragma "no doc"
proc isBoolValue(e)      param  return isBoolType(e.type);
pragma "no doc"
proc isIntValue(e)       param  return isIntType(e.type);
pragma "no doc"
proc isUintValue(e)      param  return isUintType(e.type);
pragma "no doc"
proc isRealValue(e)      param  return isRealType(e.type);
pragma "no doc"
proc isImagValue(e)      param  return isImagType(e.type);
pragma "no doc"
proc isComplexValue(e)   param  return isComplexType(e.type);
pragma "no doc"
proc isStringValue(e)    param  return isStringType(e.type);
pragma "no doc"
proc isIntegralValue(e)  param  return isIntegralType(e.type);
pragma "no doc"
proc isFloatValue(e)     param  return isFloatType(e.type);
pragma "no doc"
proc isNumericValue(e)   param  return isNumericType(e.type);
pragma "no doc"
proc isPrimitiveValue(e) param  return isPrimitiveType(e.type);
pragma "no doc"
proc isEnumValue(e)      param  return isEnumType(e.type);
//Defined elsewhere:
// isTupleValue
// isHomogeneousTupleValue
pragma "no doc"
proc isClassValue(e)     param  return isClassType(e.type);
pragma "no doc"
proc isRecordValue(e)    param  return isRecordType(e.type);
pragma "no doc"
proc isUnionValue(e)     param  return isUnionType(e.type);
//Defined elsewhere:
// isRangeValue
// isDmapValue
// isDomainValue
// isArrayValue
// isSyncValue
// isSingleValue
pragma "no doc"
proc isAtomicValue(e)    param  return isAtomicType(e.type);

//
// ixXxx() - the argument can be either a type or a value
//

// Set 1 - types.
// In chpldoc, isBool(e) et al. will apply to types as well.
pragma "no doc"
proc isBool(type t)      param  return isBoolType(t);
pragma "no doc"
proc isInt(type t)       param  return isIntType(t);
pragma "no doc"
proc isUint(type t)      param  return isUintType(t);
pragma "no doc"
proc isReal(type t)      param  return isRealType(t);
pragma "no doc"
proc isImag(type t)      param  return isImagType(t);
pragma "no doc"
proc isComplex(type t)   param  return isComplexType(t);
pragma "no doc"
proc isString(type t)    param  return isStringType(t);
pragma "no doc"
proc isIntegral(type t)  param  return isIntegralType(t);
pragma "no doc"
proc isFloat(type t)     param  return isFloatType(t);
pragma "no doc"
proc isNumeric(type t)   param  return isNumericType(t);
pragma "no doc"
proc isPrimitive(type t) param  return isPrimitiveType(t);
pragma "no doc"
proc isEnum(type t)      param  return isEnumType(t);
pragma "no doc"
proc isTuple(type t)     param  return isTupleType(t);
pragma "no doc"
proc isHomogeneousTuple(type t)  param  return isHomogeneousTupleType(t);
pragma "no doc"
proc isClass(type t)     param  return isClassType(t);
pragma "no doc"
proc isRecord(type t)    param  return isRecordType(t);
pragma "no doc"
proc isUnion(type t)     param  return isUnionType(t);
pragma "no doc"
proc isRange(type t)     param  return isRangeType(t);
pragma "no doc"
proc isDmap(type t)      param  return isDmapType(t);
pragma "no doc"
proc isDomain(type t)    param  return isDomainType(t);
pragma "no doc"
proc isArray(type t)     param  return isArrayType(t);
pragma "no doc"
proc isSync(type t)      param  return isSyncType(t);
pragma "no doc"
proc isSingle(type t)    param  return isSingleType(t);
pragma "no doc"
proc isAtomic(type t)    param  return isAtomicType(t);

// Set 2 - values.
/*
Returns `true` if the argument is a primitive type,
as defined by the language specification, or a value of a primitive type.
*/
proc isPrimitive(e) param  return isPrimitiveValue(e);
/*
Returns `true` if the argument is one the following types, of any width:
`int`, `uint`, `real`, `imag`, `complex`, or a value of such a type.
*/
proc isNumeric(e)   param  return isNumericValue(e);
/*
Returns `true` if the argument is one the following types, of any width:
`int`, `uint`, or a value of such a type.
*/
proc isIntegral(e)  param  return isIntegralValue(e);
/*
Returns `true` if the argument is one the following types, of any width:
`real`, `imag`, or a value of such a type.
*/
proc isFloat(e)     param  return isFloatValue(e);
// We document the following functions as one group, for user convenience.
proc isBool(e)      param  return isBoolValue(e);
proc isInt(e)       param  return isIntValue(e);
proc isUint(e)      param  return isUintValue(e);
proc isReal(e)      param  return isRealValue(e);
proc isImag(e)      param  return isImagValue(e);
proc isComplex(e)   param  return isComplexValue(e);
proc isString(e)    param  return isStringValue(e);
proc isEnum(e)      param  return isEnumValue(e);
proc isTuple(e)     param  return isTupleValue(e);
proc isHomogeneousTuple(e: _tuple)  param  return isHomogeneousTupleValue(e);
proc isClass(e)     param  return isClassValue(e);
proc isRecord(e)    param  return isRecordValue(e);
proc isUnion(e)     param  return isUnionValue(e);
proc isRange(e)     param  return isRangeValue(e);
proc isDmap(e)      param  return isDmapValue(e);
proc isDomain(e)    param  return isDomainValue(e);
proc isArray(e)     param  return isArrayValue(e);
pragma "no doc"
proc isSync(e: sync)     param  return true; // workaround: not isSyncValue
proc isSync(e)           param  return false;
pragma "no doc"
proc isSingle(e: single) param  return true; // workaround: not isSingleValue
proc isSingle(e)         param  return false;
// Here is a single doc comment for the above.
/*
Each of the above functions returns `true` if its argument is
a corresponding type or a value of such a type.
*/
proc isAtomic(e)    param  return isAtomicValue(e);

// for internal use until we have a better name
pragma "no doc"
proc chpl_isSyncSingleAtomic(e)         param  return false;
pragma "no doc"
proc chpl_isSyncSingleAtomic(e: sync)   param  return true;
pragma "no doc"
proc chpl_isSyncSingleAtomic(e: single) param  return true;
pragma "no doc"
proc chpl_isSyncSingleAtomic(e)  param where isAtomicType(e.type)  return true;

// Is 'sub' a subtype (or equal to) 'super'?
/* Returns `true` if the type `sub` is a subtype of the type `super`. */
proc isSubtype(type sub, type super) param where   sub: super  return true;
pragma "no doc"
proc isSubtype(type sub, type super) param where !(sub: super) return false;

// Is 'sub' a proper subtype of 'super'?
/* Returns `true` if the type `sub` is a subtype of the type `super`
   and is not `super`. */
proc isProperSubtype(type sub, type super) param
  where isSubtype(sub, super) && sub != super
  return true;
pragma "no doc"
proc isProperSubtype(type sub, type super) param
  return false;

// What follows are the type _defaultOf methods, used to initialize types
// Booleans
pragma "no doc"
inline proc _defaultOf(type t) param where (isBoolType(t)) return false:t;

// ints, reals, imags, complexes
pragma "no doc"
inline proc _defaultOf(type t) param where (isIntegralType(t)) return 0:t;
// TODO: In order to make _defaultOf param for reals and imags we had to split
// the cases into their default size and a non-param case.  It is hoped that
// in the future, floating point numbers may be castable whilst param.  In that
// world, we can again shrink these calls into the size-ignorant case.
pragma "no doc"
inline proc _defaultOf(type t) param where t == real return 0.0;
pragma "no doc"
inline proc _defaultOf(type t) where (isRealType(t) && t != real) return 0.0:t;
pragma "no doc"
inline proc _defaultOf(type t) param where t == imag return 0.0i;
pragma "no doc"
inline proc _defaultOf(type t) where (isImagType(t) && t != imag) return 0.0i:t;
// Also, complexes cannot yet be parametized
pragma "no doc"
inline proc _defaultOf(type t): t where (isComplexType(t)) {
  var ret:t = noinit;
  param floatwidth = numBits(t)/2;
  ret.re = 0.0:real(floatwidth);
  ret.im = 0.0:real(floatwidth);
  return ret;
}

// Enums
pragma "no doc"
inline proc _defaultOf(type t) param where (isEnumType(t)) {
  return chpl_enum_first(t);
}

// Classes
pragma "no doc"
inline proc _defaultOf(type t) where (isClassType(t)) return nil:t;

// Various types whose default value is known
pragma "no doc"
inline proc _defaultOf(type t) param where t: void return _void;
pragma "no doc"
inline proc _defaultOf(type t) where t: opaque return _nullOpaque;
pragma "no doc"
inline proc _defaultOf(type t) where t: chpl_taskID_t return chpl_nullTaskID;
pragma "no doc"
inline proc _defaultOf(type t) where t: _sync_aux_t return _nullSyncVarAuxFields;
pragma "no doc"
inline proc _defaultOf(type t) where t: _single_aux_t return _nullSingleVarAuxFields;
pragma "no doc"
inline proc _defaultOf(type t) where t: _task_list return _nullTaskList;

// When I finish removing PRIM_INIT before initialization to a known value, then
// this method should work.  Until then, my stopgap will be an external function
// in the runtime.
//inline proc _defaultOf(type t) where t: memory_order return memory_order_seq_cst;
pragma "no doc"
extern proc _defaultOfMemoryOrder(): memory_order;

pragma "no instantiation limit"
pragma "compiler generated"
pragma "no doc"
inline proc _defaultOf(type t) {
  select t {
    when memory_order {
      return _defaultOfMemoryOrder();
    }
    otherwise {
      return nil:t;
    }
  }
}

// Returns true if it is legal to coerce t1 to t2, false otherwise.
pragma "no doc"
proc chpl__legalIntCoerce(type t1, type t2) param
{
  if (isIntType(t2)) {
    if (isIntType(t1)) {
      return (numBits(t1) <= numBits(t2));
    } else {
      return (numBits(t1) < numBits(t2));
    }
  } else {
    if (isIntType(t1)) {
      return false;
    } else {
      return (numBits(t1) <= numBits(t2));
    }
  }
}

// Returns the type with which both s and t are compatible
// That is, both s and t can be coerced to the returned type.
pragma "no doc"
proc chpl__commonType(type s, type t) type
{
  if ! isIntegralType(s) then
    compilerError("Type ", typeToString(s) , " is non-integral: ");
  if ! isIntegralType(t) then
    compilerError("Type ", typeToString(t) , " is non-integral: ");

  if numBits(s) > numBits(t) then return s;
  if numBits(s) < numBits(t) then return t;

  if isIntType(s) && ! isIntType(t) ||
     isIntType(t) && ! isIntType(s) then
    compilerError("Types ", typeToString(s) , " and ", typeToString(t), " are incompatible.");

  return s;
}

//
// numBits(type) -- returns the number of bits in a type
//
// joint documentation, for user convenience
/*
Returns the number of bits used to store the values of type `t`.
This is available for all numeric types, fixed-width `bool` types,
and ``enum`` types.
It is not available for default-width `bool`.
*/
proc numBits(type t) param where t == bool {
  compilerError("default-width 'bool' does not have a well-defined size");
}
pragma "no doc"
proc numBits(type t) param where t == bool(8) return 8;
pragma "no doc"
proc numBits(type t) param where t == bool(16) return 16;
pragma "no doc"
proc numBits(type t) param where t == bool(32) return 32;
pragma "no doc"
proc numBits(type t) param where t == bool(64) return 64;
pragma "no doc"
proc numBits(type t) param where t == int(8) return 8;
pragma "no doc"
proc numBits(type t) param where t == int(16) return 16;
pragma "no doc"
proc numBits(type t) param where t == int(32) return 32;
pragma "no doc"
proc numBits(type t) param where t == int(64) return 64;
pragma "no doc"
proc numBits(type t) param where t == uint(8) return 8;
pragma "no doc"
proc numBits(type t) param where t == uint(16) return 16;
pragma "no doc"
proc numBits(type t) param where t == uint(32) return 32;
pragma "no doc"
proc numBits(type t) param where t == uint(64) return 64;
pragma "no doc"
proc numBits(type t) param where t == real(32) return 32;
pragma "no doc"
proc numBits(type t) param where t == real(64) return 64;
pragma "no doc"
proc numBits(type t) param where t == imag(32) return 32;
pragma "no doc"
proc numBits(type t) param where t == imag(64) return 64;
pragma "no doc"
proc numBits(type t) param where t == complex(64) return 64;
pragma "no doc"
proc numBits(type t) param where t == complex(128) return 128;

//
// numBytes(type) -- returns the number of bytes in a type
//

pragma "no doc" // do we want to document this one?
param bitsPerByte = 8;

/*
Returns the number of bytes used to store the values of type `t`.
This is available for all numeric types, fixed-width `bool` types,
and ``enum`` types.
It is not available for default-width `bool`.
*/
proc numBytes(type t) param return numBits(t)/8;

// joint documentation, for user convenience
/*
Returns the minimum value the type `t` can store.
`t` can be one of the following types, of any width:
`bool`, `int`, `uint`, `real`, `imag`, `complex`.
When `t` is a `bool` type, it returns `false`.
When `t` is `real`, `imag`, or `complex` type,
it is a non-``param`` function.
*/
proc min(type t) param  where isBool(t)      return false: t;

pragma "no doc"
proc min(type t) param  where t == int(8)    return 0x80: t;
pragma "no doc"
proc min(type t) param  where t == int(16)   return 0x8000: t;
pragma "no doc"
proc min(type t) param  where t == int(32)   return 0x80000000: t;
pragma "no doc"
proc min(type t) param  where t == int(64)   return 0x8000000000000000: t;

pragma "no doc"
proc min(type t) param  where isUint(t)      return 0: t;

pragma "no doc"
proc min(type t) where isFloatType(t)        return __primitive( "_min", t);

pragma "no doc"
proc min(type t) where isComplexType(t) {
  var x: t;
  x.re = min(x.re.type);
  x.im = min(x.im.type);
  return x;
}

// joint documentation, for user convenience
/*
Returns the minimum value the type `t` can store.
`t` can be one of the following types, of any width:
`bool`, `int`, `uint`, `real`, `imag`, `complex`.
When `t` is a `bool` type, it returns `false`.
When `t` is a `real`, `imag`, or `complex` type,
it is a non-``param`` function.
*/
proc max(type t) param  where isBool(t)      return true: t;

pragma "no doc"
proc max(type t) param  where t == int(8)    return 0x7f: t;
pragma "no doc"
proc max(type t) param  where t == int(16)   return 0x7fff: t;
pragma "no doc"
proc max(type t) param  where t == int(32)   return 0x7fffffff: t;
pragma "no doc"
proc max(type t) param  where t == int(64)   return 0x7fffffffffffffff: t;

pragma "no doc"
proc max(type t) param  where t == uint(8)   return 0xff: t;
pragma "no doc"
proc max(type t) param  where t == uint(16)  return 0xffff: t;
pragma "no doc"
proc max(type t) param  where t == uint(32)  return 0xffffffff: t;
pragma "no doc"
proc max(type t) param  where t == uint(64)  return 0xffffffffffffffff: t;

pragma "no doc"
proc max(type t) where isFloatType(t)        return __primitive( "_max", t);

pragma "no doc"
proc max(type t) where isComplexType(t) {
  var x: t;
  x.re = max(x.re.type);
  x.im = max(x.im.type);
  return x;
}

pragma "no doc"
iter chpl_enumerate(type t: enumerated) {
  const enumTuple = chpl_enum_enumerate(t);
  for i in 1..enumTuple.size do
    yield enumTuple(i);
}

// TODO add chpl_ to these functions' names - they are not intended for user.
pragma "no doc"
proc enum_minbits(type t: enumerated) param {
  return __primitive( "enum min bits", t);
}
pragma "no doc"
proc enum_issigned(type t: enumerated) param {
  return __primitive( "enum is signed", t);
}
// TODO - maybe this function can be useful for the user, for C interop?
// If so, give it a different name.
pragma "no doc"
proc enum_mintype(type t: enumerated) type {
  param minbits = enum_minbits(t);
  param signed = enum_issigned(t);
  if signed {
    return int(minbits);
  } else {
    return uint(minbits);
  }
}

pragma "no doc" // documented with the other numBits() above
proc numBits(type t: enumerated) param {
  return numBits(enum_mintype(t));
}

/*
Returns `this`, cast to the type `T`.
Generates a run-time error if `this` cannot be represented by `T`,
for example ``(-1).safeCast(uint)`` or ``256.safeCast(uint(8))``.

This method performs the minimum number of runtime checks.
For example, when casting from `uint(8)` to `uint(64)`,
no checks at all will be done.
*/
inline proc integral.safeCast(type T) : T where isUintType(T) {
  if castChecking {
    if isIntType(this.type) {
      // int(?) -> uint(?)
      if this < 0 then // runtime check
        halt("casting "+typeToString(this.type)+" less than 0 to "+typeToString(T));
    }

    if max(this.type):uint > max(T):uint {
      // [u]int(?) -> uint(?)
      if (this:uint > max(T):uint) then // runtime check
        halt("casting "+typeToString(this.type)+" with a value greater than the maximum of "+
             typeToString(T)+" to "+typeToString(T));
    }
  }
  return this:T;
}

pragma "no doc" // documented with the other safeCast above
inline proc integral.safeCast(type T) : T where isIntType(T) {
  if castChecking {
    if max(this.type):uint > max(T):uint {
      // this isUintType check lets us avoid a runtime check for this < 0
      if isUintType(this.type) {
        // uint(?) -> int(?)
        if this:uint > max(T):uint then // runtime check
          halt("casting "+typeToString(this.type)+" with a value greater than the maximum of "+
               typeToString(T)+" to "+typeToString(T));
      } else {
        // int(?) -> int(?)
        // max(T) <= max(int), so cast to int is safe
        if this:int > max(T):int then // runtime check
          halt("casting "+typeToString(this.type)+" with a value greater than the maximum of "+
               typeToString(T)+" to "+typeToString(T));
      }
    }
    if isIntType(this.type) {
      if min(this.type):int < min(T):int {
        // int(?) -> int(?)
        if this:int < min(T):int then // runtime check
          halt("casting "+typeToString(this.type)+" with a value less than the minimum of "+
               typeToString(T)+" to "+typeToString(T));
      }
    }
  }
  return this:T;
}

pragma "no doc" // documented with the other safeCast above
proc integral.safeCast(type T) {
  compilerError("safeCast is only supported between integral types");
}

//
// identity functions (for reductions)
//
pragma "no doc"
inline proc _prod_id(type t) return 1:t;
pragma "no doc"
inline proc _land_id(type t) return true;
pragma "no doc"
inline proc _lor_id(type t) return false;
pragma "no doc"
inline proc _lxor_id(type t) return false;
pragma "no doc"
inline proc _band_id(type t) {
  // MPF - this doesn't really make sense for floating-point types,
  // but old code had it casting MAX_UINT to the same type...
  // I just moved it from the code generator since we don't need
  // a primitive to generate constants.
  if t == bool then return true;
  if t == int(8) then return -1:t;
  if t == int(16) then return -1:t;
  if t == int(32) then return -1:t;
  if t == int(64) then return -1:t;
  if t == real(32) then return max(uint(32)):t;
  if t == real(64) then return max(uint(64)):t;
  if t == imag(32) then return max(uint(32)):t;
  if t == imag(64) then return max(uint(64)):t;
  if t == complex(64) then return (max(uint(32)):real(32), max(uint(32)):real(32)):t;
  if t == complex(128) then return (max(uint(64)):real(64), max(uint(64)):real(64)):t;
  return max(t);
}
pragma "no doc"
inline proc _bor_id(type t) return 0:t;
pragma "no doc"
inline proc _bxor_id(type t) return 0:t;

} // module Types

/*
 * Copyright 2004-2015 Cray Inc.
 * Other additional copyright holders may be indicated within.
 *
 * The entirety of this work is licensed under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License.
 *
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
Functions related to predefined types.

This is an automatic module, so these functions are available
to all Chapel programs.
*/
module Types {

pragma "no doc" // joint documentation with the next one
pragma "no instantiation limit"
proc isType(type t) param return true;
/* Returns `true` if the argument is a type. */
pragma "no instantiation limit"
proc isType(e) param return false;

pragma "no doc" // joint documentation with the next one
pragma "no instantiation limit"
proc isParam(type t)  param return false;
pragma "no doc" // joint documentation with the next one
pragma "no instantiation limit"
proc isParam(param p) param return true;
/* Returns `true` if the argument is a param. */
pragma "no instantiation limit"
proc isParam(e)       param return false;

// TODO eliminate this; beware of isPrimitive()
pragma "no doc"
pragma "no instantiation limit"
proc _isPrimitiveType(type t) param return
  isBoolType(t)  ||
  isIntegralType(t) ||
  isRealType(t)     ||
//To allow imag, need to define casts from primitive types into imag.
//isImagType(t)     ||
  (t == c_string);

/*
Returns `true` if the type `t` is a primitive type,
as defined by the language specification.
*/
pragma "no instantiation limit"
proc isPrimitiveType(type t) param return
  isVoidType(t) || isBoolType(t) || isIntType(t) || isUintType(t) ||
  isRealType(t) || isImagType(t) || isComplexType(t) || isStringType(t);

/*
Returns `true` if the type `t` is one the following types, of any width:
`int`, `uint`, `real`, `imag`, `complex`.
*/
pragma "no instantiation limit"
proc isNumericType(type t) param return
  isIntegralType(t) || isFloatType(t) || isComplexType(t);

/*
Returns `true` if the type `t` is one the following types, of any width:
`int`, `uint`.
*/
pragma "no instantiation limit"
proc isIntegralType(type t) param return
  isIntType(t) || isUintType(t);

/*
Returns `true` if the type `t` is one the following types, of any width:
`real`, `imag`.
*/
pragma "no instantiation limit"
proc isFloatType(type t) param return
  isRealType(t) || isImagType(t);

// We document the following functions as one group, for user convenience.
pragma "no instantiation limit"
proc isVoidType(type t) param return t == void;

pragma "no instantiation limit"
proc isBoolType(type t) param return
  (t == bool) | (t == bool(8)) | (t == bool(16)) | (t == bool(32)) | (t == bool(64));

pragma "no instantiation limit"
proc isIntType(type t) param return
  (t == int(8)) || (t == int(16)) || (t == int(32)) || (t == int(64));

pragma "no instantiation limit"
proc isUintType(type t) param return
  (t == uint(8)) || (t == uint(16)) || (t == uint(32)) || (t == uint(64));

pragma "no instantiation limit"
proc isEnumType(type t) param {
  proc isEnumHelp(type t: enumerated) param return true;
  proc isEnumHelp(type t) param return false;
  return isEnumHelp(t);
}

pragma "no instantiation limit"
proc isComplexType(type t) param return
  (t == complex(64)) | (t == complex(128));

pragma "no instantiation limit"
proc isRealType(type t) param return
  (t == real(32)) | (t == real(64));

pragma "no instantiation limit"
proc isImagType(type t) param return
  (t == imag(32)) | (t == imag(64));

// Here is a single doc comment for the above.
/*
Each of the above functions returns `true` if its argument is
a corresponding type.
The argument must be a type.
*/
pragma "no instantiation limit"
proc isStringType(type t) param return t == string;

// Returns the unsigned equivalent of the input type.
pragma "no doc"
proc chpl__unsignedType(type t) type
{
  if ! isIntegralType(t) then
    compilerError("range idxType is non-integral: ", typeToString(t));

  return uint(numBits(t));
}

// Returns the signed equivalent of the input type.
pragma "no doc"
proc chpl__signedType(type t) type
{
  if ! isIntegralType(t) then
    compilerError("range idxType is non-integral: ", typeToString(t));

  return int(numBits(t));
}

pragma "no doc"
proc chpl__maxIntTypeSameSign(type t) type {
  if ! isIntegralType(t) then
    compilerError("type t is non-integral: ", typeToString(t));

  if (isIntType(t)) then
    return int(64);
  else
    return uint(64);
}

//
// isXxxValue() - trivial implementations
// no isVoid() or isVoidValue() - it might not work
// Let these be for internal consumption for now,
// due to lack of consensus for the name.
//

pragma "no doc"
proc isBoolValue(e)      param  return isBoolType(e.type);
pragma "no doc"
proc isIntValue(e)       param  return isIntType(e.type);
pragma "no doc"
proc isUintValue(e)      param  return isUintType(e.type);
pragma "no doc"
proc isRealValue(e)      param  return isRealType(e.type);
pragma "no doc"
proc isImagValue(e)      param  return isImagType(e.type);
pragma "no doc"
proc isComplexValue(e)   param  return isComplexType(e.type);
pragma "no doc"
proc isStringValue(e)    param  return isStringType(e.type);
pragma "no doc"
proc isIntegralValue(e)  param  return isIntegralType(e.type);
pragma "no doc"
proc isFloatValue(e)     param  return isFloatType(e.type);
pragma "no doc"
proc isNumericValue(e)   param  return isNumericType(e.type);
pragma "no doc"
proc isPrimitiveValue(e) param  return isPrimitiveType(e.type);
pragma "no doc"
proc isEnumValue(e)      param  return isEnumType(e.type);
//Defined elsewhere:
// isTupleValue
// isHomogeneousTupleValue
pragma "no doc"
proc isClassValue(e)     param  return isClassType(e.type);
pragma "no doc"
proc isRecordValue(e)    param  return isRecordType(e.type);
pragma "no doc"
proc isUnionValue(e)     param  return isUnionType(e.type);
//Defined elsewhere:
// isRangeValue
// isDmapValue
// isDomainValue
// isArrayValue
// isSyncValue
// isSingleValue
pragma "no doc"
proc isAtomicValue(e)    param  return isAtomicType(e.type);

//
// ixXxx() - the argument can be either a type or a value
//

// Set 1 - types.
// In chpldoc, isBool(e) et al. will apply to types as well.
pragma "no doc"
proc isBool(type t)      param  return isBoolType(t);
pragma "no doc"
proc isInt(type t)       param  return isIntType(t);
pragma "no doc"
proc isUint(type t)      param  return isUintType(t);
pragma "no doc"
proc isReal(type t)      param  return isRealType(t);
pragma "no doc"
proc isImag(type t)      param  return isImagType(t);
pragma "no doc"
proc isComplex(type t)   param  return isComplexType(t);
pragma "no doc"
proc isString(type t)    param  return isStringType(t);
pragma "no doc"
proc isIntegral(type t)  param  return isIntegralType(t);
pragma "no doc"
proc isFloat(type t)     param  return isFloatType(t);
pragma "no doc"
proc isNumeric(type t)   param  return isNumericType(t);
pragma "no doc"
proc isPrimitive(type t) param  return isPrimitiveType(t);
pragma "no doc"
proc isEnum(type t)      param  return isEnumType(t);
pragma "no doc"
proc isTuple(type t)     param  return isTupleType(t);
pragma "no doc"
proc isHomogeneousTuple(type t)  param  return isHomogeneousTupleType(t);
pragma "no doc"
proc isClass(type t)     param  return isClassType(t);
pragma "no doc"
proc isRecord(type t)    param  return isRecordType(t);
pragma "no doc"
proc isUnion(type t)     param  return isUnionType(t);
pragma "no doc"
proc isRange(type t)     param  return isRangeType(t);
pragma "no doc"
proc isDmap(type t)      param  return isDmapType(t);
pragma "no doc"
proc isDomain(type t)    param  return isDomainType(t);
pragma "no doc"
proc isArray(type t)     param  return isArrayType(t);
pragma "no doc"
proc isSync(type t)      param  return isSyncType(t);
pragma "no doc"
proc isSingle(type t)    param  return isSingleType(t);
pragma "no doc"
proc isAtomic(type t)    param  return isAtomicType(t);

// Set 2 - values.
/*
Returns `true` if the argument is a primitive type,
as defined by the language specification, or a value of a primitive type.
*/
proc isPrimitive(e) param  return isPrimitiveValue(e);
/*
Returns `true` if the argument is one the following types, of any width:
`int`, `uint`, `real`, `imag`, `complex`, or a value of such a type.
*/
proc isNumeric(e)   param  return isNumericValue(e);
/*
Returns `true` if the argument is one the following types, of any width:
`int`, `uint`, or a value of such a type.
*/
proc isIntegral(e)  param  return isIntegralValue(e);
/*
Returns `true` if the argument is one the following types, of any width:
`real`, `imag`, or a value of such a type.
*/
proc isFloat(e)     param  return isFloatValue(e);
// We document the following functions as one group, for user convenience.
proc isBool(e)      param  return isBoolValue(e);
proc isInt(e)       param  return isIntValue(e);
proc isUint(e)      param  return isUintValue(e);
proc isReal(e)      param  return isRealValue(e);
proc isImag(e)      param  return isImagValue(e);
proc isComplex(e)   param  return isComplexValue(e);
proc isString(e)    param  return isStringValue(e);
proc isEnum(e)      param  return isEnumValue(e);
proc isTuple(e)     param  return isTupleValue(e);
proc isHomogeneousTuple(e: _tuple)  param  return isHomogeneousTupleValue(e);
proc isClass(e)     param  return isClassValue(e);
proc isRecord(e)    param  return isRecordValue(e);
proc isUnion(e)     param  return isUnionValue(e);
proc isRange(e)     param  return isRangeValue(e);
proc isDmap(e)      param  return isDmapValue(e);
proc isDomain(e)    param  return isDomainValue(e);
proc isArray(e)     param  return isArrayValue(e);
pragma "no doc"
proc isSync(e: sync)     param  return true; // workaround: not isSyncValue
proc isSync(e)           param  return false;
pragma "no doc"
proc isSingle(e: single) param  return true; // workaround: not isSingleValue
proc isSingle(e)         param  return false;
// Here is a single doc comment for the above.
/*
Each of the above functions returns `true` if its argument is
a corresponding type or a value of such a type.
*/
proc isAtomic(e)    param  return isAtomicValue(e);

// for internal use until we have a better name
pragma "no doc"
proc chpl_isSyncSingleAtomic(e)         param  return false;
pragma "no doc"
proc chpl_isSyncSingleAtomic(e: sync)   param  return true;
pragma "no doc"
proc chpl_isSyncSingleAtomic(e: single) param  return true;
pragma "no doc"
proc chpl_isSyncSingleAtomic(e)  param where isAtomicType(e.type)  return true;

// Is 'sub' a subtype (or equal to) 'super'?
/* Returns `true` if the type `sub` is a subtype of the type `super`. */
proc isSubtype(type sub, type super) param where   sub: super  return true;
pragma "no doc"
proc isSubtype(type sub, type super) param where !(sub: super) return false;

// Is 'sub' a proper subtype of 'super'?
/* Returns `true` if the type `sub` is a subtype of the type `super`
   and is not `super`. */
proc isProperSubtype(type sub, type super) param
  where isSubtype(sub, super) && sub != super
  return true;
pragma "no doc"
proc isProperSubtype(type sub, type super) param
  return false;

// What follows are the type _defaultOf methods, used to initialize types
// Booleans
pragma "no doc"
inline proc _defaultOf(type t) param where (isBoolType(t)) return false:t;

// ints, reals, imags, complexes
pragma "no doc"
inline proc _defaultOf(type t) param where (isIntegralType(t)) return 0:t;
// TODO: In order to make _defaultOf param for reals and imags we had to split
// the cases into their default size and a non-param case.  It is hoped that
// in the future, floating point numbers may be castable whilst param.  In that
// world, we can again shrink these calls into the size-ignorant case.
pragma "no doc"
inline proc _defaultOf(type t) param where t == real return 0.0;
pragma "no doc"
inline proc _defaultOf(type t) where (isRealType(t) && t != real) return 0.0:t;
pragma "no doc"
inline proc _defaultOf(type t) param where t == imag return 0.0i;
pragma "no doc"
inline proc _defaultOf(type t) where (isImagType(t) && t != imag) return 0.0i:t;
// Also, complexes cannot yet be parametized
pragma "no doc"
inline proc _defaultOf(type t): t where (isComplexType(t)) {
  var ret:t = noinit;
  param floatwidth = numBits(t)/2;
  ret.re = 0.0:real(floatwidth);
  ret.im = 0.0:real(floatwidth);
  return ret;
}

// Enums
pragma "no doc"
inline proc _defaultOf(type t) param where (isEnumType(t)) {
  return chpl_enum_first(t);
}

// Classes
pragma "no doc"
inline proc _defaultOf(type t) where (isClassType(t)) return nil:t;

// Various types whose default value is known
pragma "no doc"
inline proc _defaultOf(type t) param where t: void return _void;
pragma "no doc"
inline proc _defaultOf(type t) where t: opaque return _nullOpaque;
pragma "no doc"
inline proc _defaultOf(type t) where t: chpl_taskID_t return chpl_nullTaskID;
pragma "no doc"
inline proc _defaultOf(type t) where t: _sync_aux_t return _nullSyncVarAuxFields;
pragma "no doc"
inline proc _defaultOf(type t) where t: _single_aux_t return _nullSingleVarAuxFields;
pragma "no doc"
inline proc _defaultOf(type t) where t: _task_list return _nullTaskList;

// When I finish removing PRIM_INIT before initialization to a known value, then
// this method should work.  Until then, my stopgap will be an external function
// in the runtime.
//inline proc _defaultOf(type t) where t: memory_order return memory_order_seq_cst;
pragma "no doc"
extern proc _defaultOfMemoryOrder(): memory_order;

pragma "no instantiation limit"
pragma "compiler generated"
pragma "no doc"
inline proc _defaultOf(type t) {
  select t {
    when memory_order {
      return _defaultOfMemoryOrder();
    }
    otherwise {
      return nil:t;
    }
  }
}

// Returns true if it is legal to coerce t1 to t2, false otherwise.
pragma "no doc"
proc chpl__legalIntCoerce(type t1, type t2) param
{
  if (isIntType(t2)) {
    if (isIntType(t1)) {
      return (numBits(t1) <= numBits(t2));
    } else {
      return (numBits(t1) < numBits(t2));
    }
  } else {
    if (isIntType(t1)) {
      return false;
    } else {
      return (numBits(t1) <= numBits(t2));
    }
  }
}

// Returns the type with which both s and t are compatible
// That is, both s and t can be coerced to the returned type.
pragma "no doc"
proc chpl__commonType(type s, type t) type
{
  if ! isIntegralType(s) then
    compilerError("Type ", typeToString(s) , " is non-integral: ");
  if ! isIntegralType(t) then
    compilerError("Type ", typeToString(t) , " is non-integral: ");

  if numBits(s) > numBits(t) then return s;
  if numBits(s) < numBits(t) then return t;

  if isIntType(s) && ! isIntType(t) ||
     isIntType(t) && ! isIntType(s) then
    compilerError("Types ", typeToString(s) , " and ", typeToString(t), " are incompatible.");

  return s;
}

//
// numBits(type) -- returns the number of bits in a type
//
// joint documentation, for user convenience
/*
Returns the number of bits used to store the values of type `t`.
This is available for all numeric types, fixed-width `bool` types,
and ``enum`` types.
It is not available for default-width `bool`.
*/
proc numBits(type t) param where t == bool {
  compilerError("default-width 'bool' does not have a well-defined size");
}
pragma "no doc"
proc numBits(type t) param where t == bool(8) return 8;
pragma "no doc"
proc numBits(type t) param where t == bool(16) return 16;
pragma "no doc"
proc numBits(type t) param where t == bool(32) return 32;
pragma "no doc"
proc numBits(type t) param where t == bool(64) return 64;
pragma "no doc"
proc numBits(type t) param where t == int(8) return 8;
pragma "no doc"
proc numBits(type t) param where t == int(16) return 16;
pragma "no doc"
proc numBits(type t) param where t == int(32) return 32;
pragma "no doc"
proc numBits(type t) param where t == int(64) return 64;
pragma "no doc"
proc numBits(type t) param where t == uint(8) return 8;
pragma "no doc"
proc numBits(type t) param where t == uint(16) return 16;
pragma "no doc"
proc numBits(type t) param where t == uint(32) return 32;
pragma "no doc"
proc numBits(type t) param where t == uint(64) return 64;
pragma "no doc"
proc numBits(type t) param where t == real(32) return 32;
pragma "no doc"
proc numBits(type t) param where t == real(64) return 64;
pragma "no doc"
proc numBits(type t) param where t == imag(32) return 32;
pragma "no doc"
proc numBits(type t) param where t == imag(64) return 64;
pragma "no doc"
proc numBits(type t) param where t == complex(64) return 64;
pragma "no doc"
proc numBits(type t) param where t == complex(128) return 128;

//
// numBytes(type) -- returns the number of bytes in a type
//

pragma "no doc" // do we want to document this one?
param bitsPerByte = 8;

/*
Returns the number of bytes used to store the values of type `t`.
This is available for all numeric types, fixed-width `bool` types,
and ``enum`` types.
It is not available for default-width `bool`.
*/
proc numBytes(type t) param return numBits(t)/8;

// joint documentation, for user convenience
/*
Returns the minimum value the type `t` can store.
`t` can be one of the following types, of any width:
`bool`, `int`, `uint`, `real`, `imag`, `complex`.
When `t` is a `bool` type, it returns `false`.
When `t` is `real`, `imag`, or `complex` type,
it is a non-``param`` function.
*/
proc min(type t) param  where isBool(t)      return false: t;

pragma "no doc"
proc min(type t) param  where t == int(8)    return 0x80: t;
pragma "no doc"
proc min(type t) param  where t == int(16)   return 0x8000: t;
pragma "no doc"
proc min(type t) param  where t == int(32)   return 0x80000000: t;
pragma "no doc"
proc min(type t) param  where t == int(64)   return 0x8000000000000000: t;

pragma "no doc"
proc min(type t) param  where isUint(t)      return 0: t;

pragma "no doc"
proc min(type t) where isFloatType(t)        return __primitive( "_min", t);

pragma "no doc"
proc min(type t) where isComplexType(t) {
  var x: t;
  x.re = min(x.re.type);
  x.im = min(x.im.type);
  return x;
}

// joint documentation, for user convenience
/*
Returns the minimum value the type `t` can store.
`t` can be one of the following types, of any width:
`bool`, `int`, `uint`, `real`, `imag`, `complex`.
When `t` is a `bool` type, it returns `false`.
When `t` is a `real`, `imag`, or `complex` type,
it is a non-``param`` function.
*/
proc max(type t) param  where isBool(t)      return true: t;

pragma "no doc"
proc max(type t) param  where t == int(8)    return 0x7f: t;
pragma "no doc"
proc max(type t) param  where t == int(16)   return 0x7fff: t;
pragma "no doc"
proc max(type t) param  where t == int(32)   return 0x7fffffff: t;
pragma "no doc"
proc max(type t) param  where t == int(64)   return 0x7fffffffffffffff: t;

pragma "no doc"
proc max(type t) param  where t == uint(8)   return 0xff: t;
pragma "no doc"
proc max(type t) param  where t == uint(16)  return 0xffff: t;
pragma "no doc"
proc max(type t) param  where t == uint(32)  return 0xffffffff: t;
pragma "no doc"
proc max(type t) param  where t == uint(64)  return 0xffffffffffffffff: t;

pragma "no doc"
proc max(type t) where isFloatType(t)        return __primitive( "_max", t);

pragma "no doc"
proc max(type t) where isComplexType(t) {
  var x: t;
  x.re = max(x.re.type);
  x.im = max(x.im.type);
  return x;
}

pragma "no doc"
iter chpl_enumerate(type t: enumerated) {
  const enumTuple = chpl_enum_enumerate(t);
  for i in 1..enumTuple.size do
    yield enumTuple(i);
}

// TODO we should remove "min" from these functions' names.
/* Returns the number of bits used to store the values of type `t`. */
proc enum_minbits(type t: enumerated) param {
  return __primitive( "enum min bits", t);
}
/* Returns `true` if the integral type used to store the values of type `t`
   is signed. */
proc enum_issigned(type t: enumerated) param {
  return __primitive( "enum is signed", t);
}
/* Returns the integral type used to store the values of type `t`. */
proc enum_mintype(type t: enumerated) type {
  param minbits = enum_minbits(t);
  param signed = enum_issigned(t);
  if signed {
    return int(minbits);
  } else {
    return uint(minbits);
  }
}

pragma "no doc" // documented with the other numBits() above
proc numBits(type t: enumerated) param {
  return numBits(enum_mintype(t));
}

/*
Returns `this`, cast to the type `T`.
Generates a run-time error if `this` cannot be represented by `T`,
for example ``(-1).safeCast(uint)`` or ``256.safeCast(uint(8))``.

This method performs the minimum number of runtime checks.
For example, when casting from `uint(8)` to `uint(64)`,
no checks at all will be done.
*/
inline proc integral.safeCast(type T) : T where isUintType(T) {
  if castChecking {
    if isIntType(this.type) {
      // int(?) -> uint(?)
      if this < 0 then // runtime check
        halt("casting "+typeToString(this.type)+" less than 0 to "+typeToString(T));
    }

    if max(this.type):uint > max(T):uint {
      // [u]int(?) -> uint(?)
      if (this:uint > max(T):uint) then // runtime check
        halt("casting "+typeToString(this.type)+" with a value greater than the maximum of "+
             typeToString(T)+" to "+typeToString(T));
    }
  }
  return this:T;
}

pragma "no doc" // documented with the other safeCast above
inline proc integral.safeCast(type T) : T where isIntType(T) {
  if castChecking {
    if max(this.type):uint > max(T):uint {
      // this isUintType check lets us avoid a runtime check for this < 0
      if isUintType(this.type) {
        // uint(?) -> int(?)
        if this:uint > max(T):uint then // runtime check
          halt("casting "+typeToString(this.type)+" with a value greater than the maximum of "+
               typeToString(T)+" to "+typeToString(T));
      } else {
        // int(?) -> int(?)
        // max(T) <= max(int), so cast to int is safe
        if this:int > max(T):int then // runtime check
          halt("casting "+typeToString(this.type)+" with a value greater than the maximum of "+
               typeToString(T)+" to "+typeToString(T));
      }
    }
    if isIntType(this.type) {
      if min(this.type):int < min(T):int {
        // int(?) -> int(?)
        if this:int < min(T):int then // runtime check
          halt("casting "+typeToString(this.type)+" with a value less than the minimum of "+
               typeToString(T)+" to "+typeToString(T));
      }
    }
  }
  return this:T;
}

pragma "no doc" // documented with the other safeCast above
proc integral.safeCast(type T) {
  compilerError("safeCast is only supported between integral types");
}

//
// identity functions (for reductions)
//
pragma "no doc"
inline proc _prod_id(type t) return 1:t;
pragma "no doc"
inline proc _land_id(type t) return true;
pragma "no doc"
inline proc _lor_id(type t) return false;
pragma "no doc"
inline proc _lxor_id(type t) return false;
pragma "no doc"
inline proc _band_id(type t) {
  // MPF - this doesn't really make sense for floating-point types,
  // but old code had it casting MAX_UINT to the same type...
  // I just moved it from the code generator since we don't need
  // a primitive to generate constants.
  if t == bool then return true;
  if t == int(8) then return -1:t;
  if t == int(16) then return -1:t;
  if t == int(32) then return -1:t;
  if t == int(64) then return -1:t;
  if t == real(32) then return max(uint(32)):t;
  if t == real(64) then return max(uint(64)):t;
  if t == imag(32) then return max(uint(32)):t;
  if t == imag(64) then return max(uint(64)):t;
  if t == complex(64) then return (max(uint(32)):real(32), max(uint(32)):real(32)):t;
  if t == complex(128) then return (max(uint(64)):real(64), max(uint(64)):real(64)):t;
  return max(t);
}
pragma "no doc"
inline proc _bor_id(type t) return 0:t;
pragma "no doc"
inline proc _bxor_id(type t) return 0:t;

} // module Types


Fix a typo in which I confused a formal type declaration with a cast

/*
 * Copyright 2004-2015 Cray Inc.
 * Other additional copyright holders may be indicated within.
 *
 * The entirety of this work is licensed under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License.
 *
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Chapel Strings
module String {
  use CString;

  pragma "default string value" extern var defaultStringValue: string = noinit;

  // The following method is called by the compiler to determine the default
  // value of a given type.  For strings, this should return the value
  // defined above.
  // strings will be records, so param is not possible for them
  inline proc _defaultOf(type t) where t: string return defaultStringValue;

  // String concatenation
  inline proc +(s: string, x: string) {
    var cs = __primitive("string_concat", s.c_str(), x.c_str());
    const ret = toString(cs);
    // toString steals the c_string_copy, so no need to free it here.
    return ret;
  }

  inline proc +(s: c_string, x: string) {
    var cs = __primitive("string_concat", s, x.c_str());
    const ret = toString(cs);
    // toString steals the c_string_copy, so no need to free it here.
    return ret;
  }

  inline proc +(s: string, x: c_string) {
    var cs = __primitive("string_concat", s.c_str(), x);
    const ret = toString(cs);
    // toString steals the c_string_copy, so no need to free it here.
    return ret;
  }

  inline proc +(s: string, x: numeric)
    return s + x:string;

  inline proc +(x: numeric, s: string)
    return x:string + s;

  inline proc +(s: string, x: enumerated)
    return s + x:string;

  inline proc +(x: enumerated, s: string)
    return x:string + s;

  inline proc +(s: string, x: bool)
    return s + x:string;

  inline proc +(x: bool, s: string)
    return x:string + s;

  proc typeToString(type t) param {
    return __primitive("typeToString", t);
  }

  proc typeToString(x) param {
    compilerError("typeToString()'s argument must be a type, not a value");
  }

  // This implies that the *representation* of strings is shared.
  // If strings are reimplemented as classes or records, a less trivial
  // implementation for assignment will become necessary.
  inline proc =(ref a: string, b: string) { __primitive("move", a, b); }

  inline proc ==(a: string, b: string) return (__primitive("string_compare", a.c_str(), b.c_str()) == 0);
  inline proc !=(a: string, b: string) return (__primitive("string_compare", a.c_str(), b.c_str()) != 0);

  inline proc <=(a: string, b: string) return a.c_str()<=b.c_str();
  inline proc >=(a: string, b: string) return a.c_str()>=b.c_str();
  inline proc <(a: string, b: string) return a.c_str()<b.c_str();
  inline proc >(a: string, b: string) return a.c_str()>b.c_str();

  //
  // primitive string functions and methods
  //
  inline proc ascii(a: string) return ascii(a.c_str());
  inline proc string.length return this.c_str().length;
  inline proc string.size return this.length;
  inline proc string.substring(i: int) {
    var cs = this.c_str().substring(i);
    const ret = toString(cs);
    // toString steals the returned c_string_copy, so no need to free it.
    return ret;
  }
  inline proc string.substring(r: range(?)) {
    var cs = this.c_str().substring(r);
    const ret = toString(cs);
    // toString steals the returned c_string_copy, so no need to free it.
    return ret;
  }

  inline proc _string_contains(a: string, b: string)
    return _string_contains(a.c_str(), b.c_str());

  /* args: any number of strings
     return: Returns true if this starts with one of the strings specified in args
   */
  inline proc string.startsWith(args ...?k):bool {
    for param i in 1..k {
      if (this.substring(0..args(i).length) == args(i))
        then return true;
    }
    return false;
  }

  /* Returns the index of the first occurrence of a substring within a string,
      or 0 if the substring is not in the string.
   */
  inline proc string.indexOf(substring:string):int
    return this.c_str().indexOf(substring.c_str());
  inline proc string.indexOf(substring:c_string):int
    return this.c_str().indexOf(substring);

  // cast to and from Chapel strings use c_string
  pragma "compiler generated"
  inline proc _cast(type t, x) where t == string {
    var cs = _cast(c_string_copy, x);
    // Note, this uses a non-allocating toString(), and steals cs (no need to free).
    const ret = toString(cs);
    return ret;
  }

  // The "compiler generated" flag is used, because this cast has to be weaker
  // than the c_string-to-enum cast generated in buildDefaultFunctions().
  // The circularity is caused by the built-in c_string-to-string coercion.
  // TODO: If string literals were all actually chpl_strings, then I think the
  // coercion (and this cruft) could be removed.
  pragma "compiler generated"
  inline proc _cast(type t, x: string) where isEnumType(t)
    return _cast(t, x.c_str());

  inline proc _cast(type t, x: string) where isBoolType(t) || isNumericType(t)
    return _cast(t, x.c_str());

  //
  // casts from c_string to bool types
  //
  inline proc _cast(type t, x:c_string) where isBoolType(t)
  {
    pragma "insert line file info"
    extern proc c_string_to_chpl_bool(x:c_string) : bool;
    return c_string_to_chpl_bool(x) : t;
  }

  //
  // casts from c_string to integer types
  //
  pragma "insert line file info"
  extern proc c_string_to_int8_t  (x:c_string) : int(8);
  pragma "insert line file info"
  extern proc c_string_to_int16_t (x:c_string) : int(16);
  pragma "insert line file info"
  extern proc c_string_to_int32_t (x:c_string) : int(32);
  pragma "insert line file info"
  extern proc c_string_to_int64_t (x:c_string) : int(64);
  pragma "insert line file info"
  extern proc c_string_to_uint8_t (x:c_string) : uint(8);
  pragma "insert line file info"
  extern proc c_string_to_uint16_t(x:c_string) : uint(16);
  pragma "insert line file info"
  extern proc c_string_to_uint32_t(x:c_string) : uint(32);
  pragma "insert line file info"
  extern proc c_string_to_uint64_t(x:c_string) : uint(64);

  inline proc _cast(type t, x:c_string) where t == int(8)
    return c_string_to_int8_t(x);
  inline proc _cast(type t, x:c_string) where t == int(16)
    return c_string_to_int16_t(x);
  inline proc _cast(type t, x:c_string) where t == int(32)
    return c_string_to_int32_t(x);
  inline proc _cast(type t, x:c_string) where t == int(64)
    return c_string_to_int64_t(x);
  inline proc _cast(type t, x:c_string) where t == uint(8)
    return c_string_to_uint8_t(x);
  inline proc _cast(type t, x:c_string) where t == uint(16)
    return c_string_to_uint16_t(x);
  inline proc _cast(type t, x:c_string) where t == uint(32)
    return c_string_to_uint32_t(x);
  inline proc _cast(type t, x:c_string) where t == uint(64)
    return c_string_to_uint64_t(x);

  //
  // casts from c_string to real/imag types
  //
  pragma "insert line file info"
  extern proc c_string_to_real32(x:c_string) : real(32);
  pragma "insert line file info"
  extern proc c_string_to_real64(x:c_string) : real(64);
  pragma "insert line file info"
  extern proc c_string_to_imag32(x:c_string) : imag(32);
  pragma "insert line file info"
  extern proc c_string_to_imag64(x:c_string) : imag(64);

  inline proc _cast(type t, x:c_string) where t == real(32)
    return c_string_to_real32(x);
  inline proc _cast(type t, x:c_string) where t == real(64)
    return c_string_to_real64(x);
  inline proc _cast(type t, x:c_string) where t == imag(32)
    return c_string_to_imag32(x);
  inline proc _cast(type t, x:c_string) where t == imag(64)
    return c_string_to_imag64(x);

  //
  // casts from c_string to complex types
  //
  pragma "insert line file info"
  extern proc c_string_to_complex64(x:c_string) : complex(64);
  pragma "insert line file info"
  extern proc c_string_to_complex128(x:c_string) : complex(128);

  inline proc _cast(type t, x:c_string) where t == complex(64)
    return c_string_to_complex64(x);
  inline proc _cast(type t, x:c_string) where t == complex(128)
    return c_string_to_complex128(x);

  //
  // casts from complex
  //
  inline proc _cast(type t, x: complex(?w)) where t == c_string_copy {
    if isnan(x.re) || isnan(x.im) then
      return __primitive("string_copy", "nan");
    var re = (x.re):c_string_copy;
    var im: c_string_copy;
    var op: c_string;
    if x.im < 0 {
      im = (-x.im):c_string_copy;
      op = " - ";
    } else if im == "-0.0" {
      im = "0.0":c_string_copy;
      op = " - ";
    } else {
      im = (x.im):c_string_copy;
      op = " + ";
    }
    // TODO: Add versions of the concatenation operator that consume their
    // c_string_copy arg or args.
    var ts0 = re + op;
    chpl_free_c_string_copy(re);
    var ts1 = ts0 + im;
    chpl_free_c_string_copy(ts0);
    chpl_free_c_string_copy(im);
    const ret = ts1 + "i";
    chpl_free_c_string_copy(ts1);
    return ret;
  }
  // TODO: This is only in place to support test code in types/string/sungeun.
  // Nor user nor module code should use this cast, because it strips ownership
  // from the c_string_copy returned by the above cast without first arranging
  // for its disposal.
  inline proc _cast(type t, x:complex(?w)) where t == c_string
    return _cast(c_string_copy, x);

  pragma "init copy fn"
  inline proc chpl__initCopy(a)
    where a.type == c_string || a.type == c_string_copy {
    // Currently, string representations are shared.
    // (See note on proc =(a:string, b:string) above.)
      return a;
  }

  proc chpldev_refToString(ref arg) {

    //
    // print out the address of class references as well
    //
    proc chpldev_classToString(x: object)
      return " (class = " + __primitive("ref to string", x) + ")";
    proc chpldev_classToString(x) return "";

    return __primitive("ref to string", arg) + chpldev_classToString(arg);
  }

}

// C strings
// extern type c_string; is a built-in primitive type
//
// In terms of how they are used, c_strings are a "close to the metal"
// representation, being in essence the common NUL-terminated C string.
//
// C string copies
// extern type c_string_copy is also a built-in primitive type.
// It is the same as a c_string, but in its case represents "owned" data.
// Low-level routines that allocate string data off the heap and return a deep
// copy (including string_copy, string_concat, string_index and string_select)
// have the return type of c_string_copy to denote that ownership.
//
// The difference is ignored by the C compiler, but Chapel treats them as
// different types.  This difference allows us to have two versions of
// toString: one makes a copy of its c_string argument, the other simply
// pointer-copies its c_string_copy argument.  Both effectively return an
// "owned" C string, which is how the internal chpl_string type is currently
// interpreted.  (The new record-based string implementation has different
// rules, but still makes use of the distinction between unowned c_strings and
// owned c_string_copies.
module CString {

  // The following method is called by the compiler to determine the default
  // value of a given type.
  inline proc _defaultOf(type t) param where t: c_string return "":c_string;
  inline proc _defaultOf(type t) where t == c_string_copy return _nullString;

  inline proc toString(cstr:c_string):string {
    return __primitive("string_from_c_string", cstr, 0, 0);
  }
  inline proc toString(cstr:c_string, len:int):string {
    return __primitive("string_from_c_string", cstr, 1, len);
  }

  // These routines consume the c_string_copy argument.  In terms of MM, this
  // is the same as if the caller had called chpl_free_string_copy()
  extern proc string_from_c_string_copy(ref cstrc:c_string_copy,
                                        hasLen:bool, len:int) : string;
  inline proc toString(ref cstrc:c_string_copy) : string {
    return string_from_c_string_copy(cstrc, false, 0);
  }
  inline proc toString(ref cstrc:c_string_copy, len:int) : string {
    return string_from_c_string_copy(cstrc, true, len);
  }

  // WARNING: The bytes pointed to by the c_string return value are still owned
  // by the "this" operand.  The returned c_string should not be freed!
  inline proc string.c_str():c_string {
    return __primitive("c_string_from_string", this);
  }

  inline proc ==(param s0: c_string, param s1: c_string) param {
    return __primitive("string_compare", s0, s1) == 0;
  }

  inline proc ==(s0: c_string, s1: c_string) {
    return __primitive("string_compare", s0, s1) == 0;
  }

  inline proc ==(s0: string, s1: c_string) {
    return __primitive("string_compare", s0.c_str(), s1) == 0;
  }

  inline proc ==(s0: c_string, s1: string) {
    return __primitive("string_compare", s0, s1.c_str()) == 0;
  }

  inline proc !=(param s0: c_string, param s1: c_string) param {
    return __primitive("string_compare", s0, s1) != 0;
  }

  inline proc !=(s0: c_string, s1: c_string) {
    return __primitive("string_compare", s0, s1) != 0;
  }

  inline proc !=(s0: string, s1: c_string) {
    return __primitive("string_compare", s0.c_str(), s1) != 0;
  }

  inline proc !=(s0: c_string, s1: string) {
    return __primitive("string_compare", s0, s1.c_str()) != 0;
  }

  inline proc <=(a: c_string, b: c_string) {
    return (__primitive("string_compare", a, b) <= 0);
  }

  inline proc <=(param a: c_string, param b: c_string) param {
    return (__primitive("string_compare", a, b) <= 0);
  }

  inline proc >=(a: c_string, b: c_string) {
    return (__primitive("string_compare", a, b) >= 0);
  }

  inline proc >=(param a: c_string, param b: c_string) param {
    return (__primitive("string_compare", a, b) >= 0);
  }

  inline proc <(a: c_string, b: c_string) {
    return (__primitive("string_compare", a, b) < 0);
  }

  inline proc <(param a: c_string, param b: c_string) param {
    return (__primitive("string_compare", a, b) < 0);
  }

  inline proc >(a: c_string, b: c_string) {
    return (__primitive("string_compare", a, b) > 0);
  }

  inline proc >(param a: c_string, param b: c_string) param {
    return (__primitive("string_compare", a, b) > 0);
  }

  inline proc =(ref a: c_string, b: c_string) {
    __primitive("=", a, b);
  }

  inline proc =(ref a: c_string, b: string) {
    __primitive("=", a, b.c_str());
  }

  inline proc =(ref a: string, b: c_string) {
    __primitive("=", a, toString(b));
  }

  // Create a fresh copy of the RHS string, first releasing the LHS.
  inline proc =(ref a: c_string_copy, b: c_string) {
    chpl_free_c_string_copy(a);
    var c = __primitive("string_copy", b);
    __primitive("=", a, c);
  }
  // Assume ownership of data brought by the RHS, first releasing the LHS.
  inline proc =(ref a: c_string_copy, b: c_string_copy) {
    chpl_free_c_string_copy(a);
    __primitive("=", a, b);
  }

  // Yes this is invoked sometimes. In the long run, however,
  // we'd like the compiler to eliminate casts to the same type instead.
  inline proc _cast(type t, x: c_string) where t == c_string {
    return x;
  }

  inline proc _cast(type t, x: c_string) where t == string {
    return toString(x);
  }

  inline proc _cast(type t, ref x: c_string_copy) where t == string {
    return toString(x);
  }

  inline proc _cast(type t, x: string) where t == c_string {
    return x.c_str();
  }

  inline proc _cast(type t, x: string) where t == c_string_copy {
    return __primitive("string_copy", x);
  }

  // A c_string_copy can always be used as a c_string.
  inline proc _cast(type t, x: c_string_copy) where t == c_string {
    return __primitive("cast", t, x);
  }

  extern proc chpl_bool_to_c_string(x:bool) : c_string;
  inline proc _cast(type t, x: bool(?w)) where t == c_string {
    return chpl_bool_to_c_string(x:bool);
  }
  inline proc _cast(type t, x: bool(?w)) where t == c_string_copy {
    return __primitive("string_copy", chpl_bool_to_c_string(x:bool));
  }

  inline proc _cast(type t, x:enumerated) where t == c_string_copy {
    // Use the compiler-generated enum to c_string conversion.
    var cs = _cast(c_string, x);
    return __primitive("string_copy", cs);
  }

  inline proc _cast(type t, x:integral) where t == c_string_copy {
    extern proc integral_to_c_string_copy(x:int(64), size:size_t, isSigned: bool) : c_string_copy ;
    return integral_to_c_string_copy(x:int(64), numBytes(x.type), isIntType(x.type));
  }

  extern proc real_to_c_string_copy(x:real(64), isImag: bool) : c_string_copy ;
  //
  // casts from real
  //
  inline proc _cast(type t, x:real(?w)) where t == c_string_copy {
    return real_to_c_string_copy(x:real(64), false);
  }
  // TODO: This is only in place to support test code in types/string/sungeun.
  // Nor user nor module code should use this cast, because it strips ownership
  // from the c_string_copy returned by the above cast without first arranging
  // for its disposal.
  inline proc _cast(type t, x:real(?w)) where t == c_string
    return _cast(c_string_copy, x);

  //
  // casts from imag
  //
  inline proc _cast(type t, x:imag(?w)) where t == c_string_copy {
    // The Chapel version of the imag --> real cast smashes it flat rather than
    // just stripping off the "i".  See ChapelBase:965.
    var r = __primitive("cast", real(64), x);
    return real_to_c_string_copy(r, true);
  }
  // TODO: This is only in place to support test code in types/string/sungeun.
  // Nor user nor module code should use this cast, because it strips ownership
  // from the c_string_copy returned by the above cast without first arranging
  // for its disposal.
  inline proc _cast(type t, x:imag(?w)) where t == c_string
    return _cast(c_string_copy, x);

  // Only support param c_string concatenation (for now)
  inline proc +(param a: c_string, param b: c_string) param
    return __primitive("string_concat", a, b);

  inline proc +(param s: c_string, param x: integral) param
    return __primitive("string_concat", s, x:c_string);

  inline proc +(param x: integral, param s: c_string) param
    return __primitive("string_concat", x:c_string, s);

  inline proc +(param s: c_string, param x: enumerated) param
    return __primitive("string_concat", s, x:c_string);

  inline proc +(param x: enumerated, param s: c_string) param
    return __primitive("string_concat", x:c_string, s);

  inline proc +(param s: c_string, param x: bool) param
    return __primitive("string_concat", s, x:c_string);

  inline proc +(param x: bool, param s: c_string) param
    return __primitive("string_concat", x:c_string, s);

  inline proc +(a: c_string, b: c_string)
    return __primitive("string_concat", a, b);

  proc c_string.writeThis(x: Writer) {
    x.write(this);
  }
  // The c_string_copy version is required, since apparently coercions are not
  // applied to "this".
  proc c_string_copy.writeThis(x: Writer) {
    x.write(this:c_string);
  }

  //
  // primitive c_string functions and methods
  //
  inline proc ascii(param a: c_string) param return __primitive("ascii", a);
  inline proc ascii(a: c_string) return __primitive("ascii", a);
  inline proc c_string.length return __primitive("string_length", this);
  inline proc c_string.size return this.length;
  inline proc c_string.substring(i: int)
    return __primitive("string_index", this, i);
  inline proc c_string.substring(r: range(?)) {
    var r2 = r[1..this.length];  // This may warn about ambiguously aligned ranges.
    var lo:int = r2.alignedLow, hi:int = r2.alignedHigh;
    return __primitive("string_select", this, lo, hi, r2.stride);
  }

  inline proc _string_contains(a: string, b: string)
    return __primitive("string_contains", a, b);

  inline proc param c_string.length param
    return __primitive("string_length", this);
  inline proc _string_contains(param a: c_string, param b: c_string) param
    return __primitive("string_contains", a, b);

  /* Returns the index of the first occurrence of a substring within a string,
     or 0 if the substring is not in the string.
  */
  inline proc c_string.indexOf(substring:c_string):int
    return string_index_of(this, substring);
  extern proc string_index_of(haystack:c_string, needle:c_string):int;

  // Use with care.  Not for the weak.
  inline proc chpl_free_c_string_copy(ref cs: c_string_copy) {
    pragma "insert line file info"
    extern proc chpl_rt_free_c_string(ref cs: c_string_copy);
    if (cs != _nullString) then chpl_rt_free_c_string(cs);
    // cs = _nullString;
  }

}

/*
 * Copyright 2004-2015 Cray Inc.
 * Other additional copyright holders may be indicated within.
 *
 * The entirety of this work is licensed under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License.
 *
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Chapel Strings
module String {
  use CString;

  pragma "default string value" extern var defaultStringValue: string = noinit;

  // The following method is called by the compiler to determine the default
  // value of a given type.  For strings, this should return the value
  // defined above.
  // strings will be records, so param is not possible for them
  inline proc _defaultOf(type t) where t: string return defaultStringValue;

  // String concatenation
  inline proc +(s: string, x: string) {
    var cs = __primitive("string_concat", s.c_str(), x.c_str());
    const ret = toString(cs);
    // toString steals the c_string_copy, so no need to free it here.
    return ret;
  }

  inline proc +(s: c_string, x: string) {
    var cs = __primitive("string_concat", s, x.c_str());
    const ret = toString(cs);
    // toString steals the c_string_copy, so no need to free it here.
    return ret;
  }

  inline proc +(s: string, x: c_string) {
    var cs = __primitive("string_concat", s.c_str(), x);
    const ret = toString(cs);
    // toString steals the c_string_copy, so no need to free it here.
    return ret;
  }

  inline proc +(s: string, x: numeric)
    return s + x:string;

  inline proc +(x: numeric, s: string)
    return x:string + s;

  inline proc +(s: string, x: enumerated)
    return s + x:string;

  inline proc +(x: enumerated, s: string)
    return x:string + s;

  inline proc +(s: string, x: bool)
    return s + x:string;

  inline proc +(x: bool, s: string)
    return x:string + s;

  proc typeToString(type t) param {
    return __primitive("typeToString", t);
  }

  proc typeToString(x) param {
    compilerError("typeToString()'s argument must be a type, not a value");
  }

  // This implies that the *representation* of strings is shared.
  // If strings are reimplemented as classes or records, a less trivial
  // implementation for assignment will become necessary.
  inline proc =(ref a: string, b: string) { __primitive("move", a, b); }

  inline proc ==(a: string, b: string) return (__primitive("string_compare", a.c_str(), b.c_str()) == 0);
  inline proc !=(a: string, b: string) return (__primitive("string_compare", a.c_str(), b.c_str()) != 0);

  inline proc <=(a: string, b: string) return a.c_str()<=b.c_str();
  inline proc >=(a: string, b: string) return a.c_str()>=b.c_str();
  inline proc <(a: string, b: string) return a.c_str()<b.c_str();
  inline proc >(a: string, b: string) return a.c_str()>b.c_str();

  //
  // primitive string functions and methods
  //
  inline proc ascii(a: string) return ascii(a.c_str());
  inline proc string.length return this.c_str().length;
  inline proc string.size return this.length;
  inline proc string.substring(i: int) {
    var cs = this.c_str().substring(i);
    const ret = toString(cs);
    // toString steals the returned c_string_copy, so no need to free it.
    return ret;
  }
  inline proc string.substring(r: range(?)) {
    var cs = this.c_str().substring(r);
    const ret = toString(cs);
    // toString steals the returned c_string_copy, so no need to free it.
    return ret;
  }

  inline proc _string_contains(a: string, b: string)
    return _string_contains(a.c_str(), b.c_str());

  /* args: any number of strings
     return: Returns true if this starts with one of the strings specified in args
   */
  inline proc string.startsWith(args ...?k):bool {
    for param i in 1..k {
      if (this.substring(0..args(i).length) == args(i))
        then return true;
    }
    return false;
  }

  /* Returns the index of the first occurrence of a substring within a string,
      or 0 if the substring is not in the string.
   */
  inline proc string.indexOf(substring:string):int
    return this.c_str().indexOf(substring.c_str());
  inline proc string.indexOf(substring:c_string):int
    return this.c_str().indexOf(substring);

  // cast to and from Chapel strings use c_string
  pragma "compiler generated"
  inline proc _cast(type t, x) where t == string {
    var cs = _cast(c_string_copy, x);
    // Note, this uses a non-allocating toString(), and steals cs (no need to free).
    const ret = toString(cs);
    return ret;
  }

  // The "compiler generated" flag is used, because this cast has to be weaker
  // than the c_string-to-enum cast generated in buildDefaultFunctions().
  // The circularity is caused by the built-in c_string-to-string coercion.
  // TODO: If string literals were all actually chpl_strings, then I think the
  // coercion (and this cruft) could be removed.
  pragma "compiler generated"
  inline proc _cast(type t, x: string) where isEnumType(t)
    return _cast(t, x.c_str());

  inline proc _cast(type t, x: string) where isBoolType(t) || isNumericType(t)
    return _cast(t, x.c_str());

  //
  // casts from c_string to bool types
  //
  inline proc _cast(type t, x:c_string) where isBoolType(t)
  {
    pragma "insert line file info"
    extern proc c_string_to_chpl_bool(x:c_string) : bool;
    return c_string_to_chpl_bool(x) : t;
  }

  //
  // casts from c_string to integer types
  //
  pragma "insert line file info"
  extern proc c_string_to_int8_t  (x:c_string) : int(8);
  pragma "insert line file info"
  extern proc c_string_to_int16_t (x:c_string) : int(16);
  pragma "insert line file info"
  extern proc c_string_to_int32_t (x:c_string) : int(32);
  pragma "insert line file info"
  extern proc c_string_to_int64_t (x:c_string) : int(64);
  pragma "insert line file info"
  extern proc c_string_to_uint8_t (x:c_string) : uint(8);
  pragma "insert line file info"
  extern proc c_string_to_uint16_t(x:c_string) : uint(16);
  pragma "insert line file info"
  extern proc c_string_to_uint32_t(x:c_string) : uint(32);
  pragma "insert line file info"
  extern proc c_string_to_uint64_t(x:c_string) : uint(64);

  inline proc _cast(type t, x:c_string) where t == int(8)
    return c_string_to_int8_t(x);
  inline proc _cast(type t, x:c_string) where t == int(16)
    return c_string_to_int16_t(x);
  inline proc _cast(type t, x:c_string) where t == int(32)
    return c_string_to_int32_t(x);
  inline proc _cast(type t, x:c_string) where t == int(64)
    return c_string_to_int64_t(x);
  inline proc _cast(type t, x:c_string) where t == uint(8)
    return c_string_to_uint8_t(x);
  inline proc _cast(type t, x:c_string) where t == uint(16)
    return c_string_to_uint16_t(x);
  inline proc _cast(type t, x:c_string) where t == uint(32)
    return c_string_to_uint32_t(x);
  inline proc _cast(type t, x:c_string) where t == uint(64)
    return c_string_to_uint64_t(x);

  //
  // casts from c_string to real/imag types
  //
  pragma "insert line file info"
  extern proc c_string_to_real32(x:c_string) : real(32);
  pragma "insert line file info"
  extern proc c_string_to_real64(x:c_string) : real(64);
  pragma "insert line file info"
  extern proc c_string_to_imag32(x:c_string) : imag(32);
  pragma "insert line file info"
  extern proc c_string_to_imag64(x:c_string) : imag(64);

  inline proc _cast(type t, x:c_string) where t == real(32)
    return c_string_to_real32(x);
  inline proc _cast(type t, x:c_string) where t == real(64)
    return c_string_to_real64(x);
  inline proc _cast(type t, x:c_string) where t == imag(32)
    return c_string_to_imag32(x);
  inline proc _cast(type t, x:c_string) where t == imag(64)
    return c_string_to_imag64(x);

  //
  // casts from c_string to complex types
  //
  pragma "insert line file info"
  extern proc c_string_to_complex64(x:c_string) : complex(64);
  pragma "insert line file info"
  extern proc c_string_to_complex128(x:c_string) : complex(128);

  inline proc _cast(type t, x:c_string) where t == complex(64)
    return c_string_to_complex64(x);
  inline proc _cast(type t, x:c_string) where t == complex(128)
    return c_string_to_complex128(x);

  //
  // casts from complex
  //
  inline proc _cast(type t, x: complex(?w)) where t == c_string_copy {
    if isnan(x.re) || isnan(x.im) then
      return __primitive("string_copy", "nan");
    var re = (x.re):c_string_copy;
    var im: c_string_copy;
    var op: c_string;
    if x.im < 0 {
      im = (-x.im):c_string_copy;
      op = " - ";
    } else if im == "-0.0" {
      im = "0.0":c_string_copy;
      op = " - ";
    } else {
      im = (x.im):c_string_copy;
      op = " + ";
    }
    // TODO: Add versions of the concatenation operator that consume their
    // c_string_copy arg or args.
    var ts0 = re + op;
    chpl_free_c_string_copy(re);
    var ts1 = ts0 + im;
    chpl_free_c_string_copy(ts0);
    chpl_free_c_string_copy(im);
    const ret = ts1 + "i";
    chpl_free_c_string_copy(ts1);
    return ret;
  }
  // TODO: This is only in place to support test code in types/string/sungeun.
  // Nor user nor module code should use this cast, because it strips ownership
  // from the c_string_copy returned by the above cast without first arranging
  // for its disposal.
  inline proc _cast(type t, x:complex(?w)) where t == c_string
    return _cast(c_string_copy, x);

  pragma "init copy fn"
  inline proc chpl__initCopy(a)
    where a.type == c_string || a.type == c_string_copy {
    // Currently, string representations are shared.
    // (See note on proc =(a:string, b:string) above.)
      return a;
  }

  proc chpldev_refToString(ref arg) {

    //
    // print out the address of class references as well
    //
    proc chpldev_classToString(x: object)
      return " (class = " + __primitive("ref to string", x) + ")";
    proc chpldev_classToString(x) return "";

    return __primitive("ref to string", arg) + chpldev_classToString(arg);
  }

}

// C strings
// extern type c_string; is a built-in primitive type
//
// In terms of how they are used, c_strings are a "close to the metal"
// representation, being in essence the common NUL-terminated C string.
//
// C string copies
// extern type c_string_copy is also a built-in primitive type.
// It is the same as a c_string, but in its case represents "owned" data.
// Low-level routines that allocate string data off the heap and return a deep
// copy (including string_copy, string_concat, string_index and string_select)
// have the return type of c_string_copy to denote that ownership.
//
// The difference is ignored by the C compiler, but Chapel treats them as
// different types.  This difference allows us to have two versions of
// toString: one makes a copy of its c_string argument, the other simply
// pointer-copies its c_string_copy argument.  Both effectively return an
// "owned" C string, which is how the internal chpl_string type is currently
// interpreted.  (The new record-based string implementation has different
// rules, but still makes use of the distinction between unowned c_strings and
// owned c_string_copies.
module CString {

  // The following method is called by the compiler to determine the default
  // value of a given type.
  inline proc _defaultOf(type t) param where t: c_string return "":c_string;
  inline proc _defaultOf(type t) where t == c_string_copy return _nullString;

  inline proc toString(cstr:c_string):string {
    return __primitive("string_from_c_string", cstr, 0, 0);
  }
  inline proc toString(cstr:c_string, len:int):string {
    return __primitive("string_from_c_string", cstr, 1, len);
  }

  // These routines consume the c_string_copy argument.  In terms of MM, this
  // is the same as if the caller had called chpl_free_string_copy()
  extern proc string_from_c_string_copy(ref cstrc:c_string_copy,
                                        hasLen:bool, len:int) : string;
  inline proc toString(ref cstrc:c_string_copy) : string {
    return string_from_c_string_copy(cstrc, false, 0);
  }
  inline proc toString(ref cstrc:c_string_copy, len:int) : string {
    return string_from_c_string_copy(cstrc, true, len);
  }

  // WARNING: The bytes pointed to by the c_string return value are still owned
  // by the "this" operand.  The returned c_string should not be freed!
  inline proc string.c_str():c_string {
    return __primitive("c_string_from_string", this);
  }

  inline proc ==(param s0: c_string, param s1: c_string) param {
    return __primitive("string_compare", s0, s1) == 0;
  }

  inline proc ==(s0: c_string, s1: c_string) {
    return __primitive("string_compare", s0, s1) == 0;
  }

  inline proc ==(s0: string, s1: c_string) {
    return __primitive("string_compare", s0.c_str(), s1) == 0;
  }

  inline proc ==(s0: c_string, s1: string) {
    return __primitive("string_compare", s0, s1.c_str()) == 0;
  }

  inline proc !=(param s0: c_string, param s1: c_string) param {
    return __primitive("string_compare", s0, s1) != 0;
  }

  inline proc !=(s0: c_string, s1: c_string) {
    return __primitive("string_compare", s0, s1) != 0;
  }

  inline proc !=(s0: string, s1: c_string) {
    return __primitive("string_compare", s0.c_str(), s1) != 0;
  }

  inline proc !=(s0: c_string, s1: string) {
    return __primitive("string_compare", s0, s1.c_str()) != 0;
  }

  inline proc <=(a: c_string, b: c_string) {
    return (__primitive("string_compare", a, b) <= 0);
  }

  inline proc <=(param a: c_string, param b: c_string) param {
    return (__primitive("string_compare", a, b) <= 0);
  }

  inline proc >=(a: c_string, b: c_string) {
    return (__primitive("string_compare", a, b) >= 0);
  }

  inline proc >=(param a: c_string, param b: c_string) param {
    return (__primitive("string_compare", a, b) >= 0);
  }

  inline proc <(a: c_string, b: c_string) {
    return (__primitive("string_compare", a, b) < 0);
  }

  inline proc <(param a: c_string, param b: c_string) param {
    return (__primitive("string_compare", a, b) < 0);
  }

  inline proc >(a: c_string, b: c_string) {
    return (__primitive("string_compare", a, b) > 0);
  }

  inline proc >(param a: c_string, param b: c_string) param {
    return (__primitive("string_compare", a, b) > 0);
  }

  inline proc =(ref a: c_string, b: c_string) {
    __primitive("=", a, b);
  }

  inline proc =(ref a: c_string, b: string) {
    __primitive("=", a, b.c_str());
  }

  inline proc =(ref a: string, b: c_string) {
    __primitive("=", a, toString(b));
  }

  // Create a fresh copy of the RHS string, first releasing the LHS.
  inline proc =(ref a: c_string_copy, b: c_string) {
    chpl_free_c_string_copy(a);
    var c = __primitive("string_copy", b);
    __primitive("=", a, c);
  }
  // Assume ownership of data brought by the RHS, first releasing the LHS.
  inline proc =(ref a: c_string_copy, b: c_string_copy) {
    chpl_free_c_string_copy(a);
    __primitive("=", a, b);
  }

  // Yes this is invoked sometimes. In the long run, however,
  // we'd like the compiler to eliminate casts to the same type instead.
  inline proc _cast(type t, x: c_string) where t == c_string {
    return x;
  }

  inline proc _cast(type t, x: c_string) where t == string {
    return toString(x);
  }

  inline proc _cast(type t, ref x: c_string_copy) where t == string {
    return toString(x);
  }

  inline proc _cast(type t, x: string) where t == c_string {
    return x.c_str();
  }

  inline proc _cast(type t, x: string) where t == c_string_copy {
    return __primitive("string_copy", x);
  }

  // A c_string_copy can always be used as a c_string.
  inline proc _cast(type t, x: c_string_copy) where t == c_string {
    return __primitive("cast", t, x);
  }

  extern proc chpl_bool_to_c_string(x:bool) : c_string;
  inline proc _cast(type t, x: bool(?w)) where t == c_string {
    return chpl_bool_to_c_string(x:bool);
  }
  inline proc _cast(type t, x: bool(?w)) where t == c_string_copy {
    return __primitive("string_copy", chpl_bool_to_c_string(x:bool));
  }

  inline proc _cast(type t, x:enumerated) where t == c_string_copy {
    // Use the compiler-generated enum to c_string conversion.
    var cs = _cast(c_string, x);
    return __primitive("string_copy", cs);
  }

  inline proc _cast(type t, x:integral) where t == c_string_copy {
    extern proc integral_to_c_string_copy(x:int(64), size.safeCast(size_t), isSigned: bool) : c_string_copy ;
    return integral_to_c_string_copy(x:int(64), numBytes(x.type), isIntType(x.type));
  }

  extern proc real_to_c_string_copy(x:real(64), isImag: bool) : c_string_copy ;
  //
  // casts from real
  //
  inline proc _cast(type t, x:real(?w)) where t == c_string_copy {
    return real_to_c_string_copy(x:real(64), false);
  }
  // TODO: This is only in place to support test code in types/string/sungeun.
  // Nor user nor module code should use this cast, because it strips ownership
  // from the c_string_copy returned by the above cast without first arranging
  // for its disposal.
  inline proc _cast(type t, x:real(?w)) where t == c_string
    return _cast(c_string_copy, x);

  //
  // casts from imag
  //
  inline proc _cast(type t, x:imag(?w)) where t == c_string_copy {
    // The Chapel version of the imag --> real cast smashes it flat rather than
    // just stripping off the "i".  See ChapelBase:965.
    var r = __primitive("cast", real(64), x);
    return real_to_c_string_copy(r, true);
  }
  // TODO: This is only in place to support test code in types/string/sungeun.
  // Nor user nor module code should use this cast, because it strips ownership
  // from the c_string_copy returned by the above cast without first arranging
  // for its disposal.
  inline proc _cast(type t, x:imag(?w)) where t == c_string
    return _cast(c_string_copy, x);

  // Only support param c_string concatenation (for now)
  inline proc +(param a: c_string, param b: c_string) param
    return __primitive("string_concat", a, b);

  inline proc +(param s: c_string, param x: integral) param
    return __primitive("string_concat", s, x:c_string);

  inline proc +(param x: integral, param s: c_string) param
    return __primitive("string_concat", x:c_string, s);

  inline proc +(param s: c_string, param x: enumerated) param
    return __primitive("string_concat", s, x:c_string);

  inline proc +(param x: enumerated, param s: c_string) param
    return __primitive("string_concat", x:c_string, s);

  inline proc +(param s: c_string, param x: bool) param
    return __primitive("string_concat", s, x:c_string);

  inline proc +(param x: bool, param s: c_string) param
    return __primitive("string_concat", x:c_string, s);

  inline proc +(a: c_string, b: c_string)
    return __primitive("string_concat", a, b);

  proc c_string.writeThis(x: Writer) {
    x.write(this);
  }
  // The c_string_copy version is required, since apparently coercions are not
  // applied to "this".
  proc c_string_copy.writeThis(x: Writer) {
    x.write(this:c_string);
  }

  //
  // primitive c_string functions and methods
  //
  inline proc ascii(param a: c_string) param return __primitive("ascii", a);
  inline proc ascii(a: c_string) return __primitive("ascii", a);
  inline proc c_string.length return __primitive("string_length", this);
  inline proc c_string.size return this.length;
  inline proc c_string.substring(i: int)
    return __primitive("string_index", this, i);
  inline proc c_string.substring(r: range(?)) {
    var r2 = r[1..this.length];  // This may warn about ambiguously aligned ranges.
    var lo:int = r2.alignedLow, hi:int = r2.alignedHigh;
    return __primitive("string_select", this, lo, hi, r2.stride);
  }

  inline proc _string_contains(a: string, b: string)
    return __primitive("string_contains", a, b);

  inline proc param c_string.length param
    return __primitive("string_length", this);
  inline proc _string_contains(param a: c_string, param b: c_string) param
    return __primitive("string_contains", a, b);

  /* Returns the index of the first occurrence of a substring within a string,
     or 0 if the substring is not in the string.
  */
  inline proc c_string.indexOf(substring:c_string):int
    return string_index_of(this, substring);
  extern proc string_index_of(haystack:c_string, needle:c_string):int;

  // Use with care.  Not for the weak.
  inline proc chpl_free_c_string_copy(ref cs: c_string_copy) {
    pragma "insert line file info"
    extern proc chpl_rt_free_c_string(ref cs: c_string_copy);
    if (cs != _nullString) then chpl_rt_free_c_string(cs);
    // cs = _nullString;
  }

}