text: //
  //Copyright (C) 2002-2005  3Dlabs Inc. Ltd.
  //Copyright (C) 2012-2013 LunarG, Inc.
  //
  //All rights reserved.
  //
  //Redistribution and use in source and binary forms, with or without
  //modification, are permitted provided that the following conditions
  //are met:
  //
  //    Redistributions of source code must retain the above copyright
  //    notice, this list of conditions and the following disclaimer.
  //
  //    Redistributions in binary form must reproduce the above
  //    copyright notice, this list of conditions and the following
  //    disclaimer in the documentation and/or other materials provided
  //    with the distribution.
  //
  //    Neither the name of 3Dlabs Inc. Ltd. nor the names of its
  //    contributors may be used to endorse or promote products derived
  //    from this software without specific prior written permission.
  //
  //THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  //"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  //LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  //FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  //COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  //INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  //BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  //LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  //CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  //LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  //ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  //POSSIBILITY OF SUCH DAMAGE.
  //
  
  #ifndef _TYPES_INCLUDED
  #define _TYPES_INCLUDED
  
  #include "../Include/Common.h"
  #include "../Include/BaseTypes.h"
  #include "../Public/ShaderLang.h"
  
  namespace glslang {
  
  const int GlslangMaxTypeLength = 200;  // TODO: need to print block/struct one member per line, so this can stay bounded
  
  const char* const AnonymousPrefix = "anon@"; // for something like a block whose members can be directly accessed
  inline bool IsAnonymous(const TString& name)
  {
      return name.compare(0, 5, AnonymousPrefix) == 0;
  }
  
  //
  // Details within a sampler type
  //
  enum TSamplerDim {
      EsdNone,
      Esd1D,
      Esd2D,
      Esd3D,
      EsdCube,
      EsdRect,
      EsdBuffer,
      EsdNumDims
  };
  
  struct TSampler {
      TBasicType type : 8;  // type returned by sampler
      TSamplerDim dim : 8;
      bool    arrayed : 1;
      bool     shadow : 1;
      bool         ms : 1;
      bool      image : 1;
      bool   external : 1;  // GL_OES_EGL_image_external
  
      void clear()
      {
          type = EbtVoid;
          dim = EsdNone;
          arrayed = false;
          shadow = false;
          ms = false;
          image = false;
          external = false;
      }
  
      void set(TBasicType t, TSamplerDim d, bool a = false, bool s = false, bool m = false)
      {
          type = t;
          dim = d;
          arrayed = a;
          shadow = s;
          ms = m;
          image = false;
          external = false;
      }
  
      void setImage(TBasicType t, TSamplerDim d, bool a = false, bool s = false, bool m = false)
      {
          type = t;
          dim = d;
          arrayed = a;
          shadow = s;
          ms = m;
          image = true;
          external = false;
      }
  
      bool operator==(const TSampler& right) const
      {
          return type == right.type &&
                  dim == right.dim &&
              arrayed == right.arrayed &&
               shadow == right.shadow &&
                   ms == right.ms &&
                image == right.image &&
             external == right.external;
      }
  
      TString getString() const
      {
          TString s;
  
          switch (type) {
          case EbtFloat:               break;
          case EbtInt:  s.append("i"); break;
          case EbtUint: s.append("u"); break;
          default:  break;  // some compilers want this
          }
          if (image)
              s.append("image");
          else
              s.append("sampler");
          if (external) {
              s.append("ExternalOES");
              return s;
          }
          switch (dim) {
          case Esd1D:      s.append("1D");     break;
          case Esd2D:      s.append("2D");     break;
          case Esd3D:      s.append("3D");     break;
          case EsdCube:    s.append("Cube");   break;
          case EsdRect:    s.append("2DRect"); break;
          case EsdBuffer:  s.append("Buffer"); break;
          default:  break;  // some compilers want this
          }
          if (ms)
              s.append("MS");
          if (arrayed)
              s.append("Array");
          if (shadow)
              s.append("Shadow");
  
          return s;
      }
  };
  
  //
  // Need to have association of line numbers to types in a list for building structs.
  //
  class TType;
  struct TTypeLoc {
      TType* type;
      TSourceLoc loc;
  };
  typedef TVector<TTypeLoc> TTypeList;
  
  typedef TVector<TString*> TIdentifierList;
  
  //
  // TODO: memory: TArraySizes can be replaced by something smaller.
  // Almost all arrays could be handled by two sizes each fitting
  // in 16 bits, needing a real vector only in the cases where there
  // are more than 3 sizes or a size needing more than 16 bits.
  //
  // The type is a pointer, so that it can be non-allocated and zero
  // for the vast majority of non-array types.  Note that means if it
  // is used, it will be containing at least one size.
  
  struct TArraySizes {
      POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator())
  
      TArraySizes() : implicitArraySize(1) { }
      int getSize() { return sizes.front(); }  // TArraySizes only exists if there is at least one dimension
      void setSize(int s) { sizes.push_back(s); }
      bool isArrayOfArrays() { return sizes.size() > 1; }
  protected:
      TVector<int> sizes;
      friend class TType;
      int implicitArraySize; // for tracking maximum referenced index, before an explicit size is given
  };
  
  //
  // Following are a series of helper enums for managing layouts and qualifiers,
  // used for TPublicType, TType, others.
  //
  
  enum TLayoutPacking {
      ElpNone,
      ElpShared,      // default, but different than saying nothing
      ElpStd140,
      ElpStd430,
      ElpPacked
      // If expanding, see bitfield width below
  };
  
  enum TLayoutMatrix {
      ElmNone,
      ElmRowMajor,
      ElmColumnMajor  // default, but different than saying nothing
      // If expanding, see bitfield width below
  };
  
  // Union of geometry shader and tessellation shader geometry types.
  // They don't go into TType, but rather have current state per shader or
  // active parser type (TPublicType).
  enum TLayoutGeometry {
      ElgNone,
      ElgPoints,
      ElgLines,
      ElgLinesAdjacency,
      ElgLineStrip,
      ElgTriangles,
      ElgTrianglesAdjacency,
      ElgTriangleStrip,
      ElgQuads,
      ElgIsolines,
  };
  
  enum TVertexSpacing {
      EvsNone,
      EvsEqual,
      EvsFractionalEven,
      EvsFractionalOdd
  };
  
  enum TVertexOrder {
      EvoNone,
      EvoCw,
      EvoCcw
  };
  
  // Note: order matters, as type of format is done by comparison.
  enum TLayoutFormat {
      ElfNone,
  
      // Float image
      ElfRgba32f,
      ElfRgba16f,
      ElfR32f,
      ElfRgba8,
      ElfRgba8Snorm,
  
      ElfEsFloatGuard,    // to help with comparisons
  
      ElfRg32f,
      ElfRg16f,
      ElfR11fG11fB10f,
      ElfR16f,
      ElfRgba16,
      ElfRgb10A2,
      ElfRg16,
      ElfRg8,
      ElfR16,
      ElfR8,
      ElfRgba16Snorm,
      ElfRg16Snorm,
      ElfRg8Snorm,
      ElfR16Snorm,
      ElfR8Snorm,
  
      ElfFloatGuard,      // to help with comparisons
  
      // Int image
      ElfRgba32i,
      ElfRgba16i,
      ElfRgba8i,
      ElfR32i,
  
      ElfEsIntGuard,     // to help with comparisons
  
      ElfRg32i,
      ElfRg16i,
      ElfRg8i,
      ElfR16i,
      ElfR8i,
  
      ElfIntGuard,       // to help with comparisons
  
      // Uint image
      ElfRgba32ui,
      ElfRgba16ui,
      ElfRgba8ui,
      ElfR32ui,
  
      ElfEsUintGuard,    // to help with comparisons
  
      ElfRg32ui,
      ElfRg16ui,
      ElfRg8ui,
      ElfR16ui,
      ElfR8ui,
  
      ElfCount
  };
  
  enum TLayoutDepth {
      EldNone,
      EldAny,
      EldGreater,
      EldLess,
      EldUnchanged,
      
      EldCount
  };
  
  class TQualifier {
  public:
      void clear()
      {
          precision = EpqNone;
          invariant = false;
          makeTemporary();
      }
  
      // drop qualifiers that don't belong in a temporary variable
      void makeTemporary()
      {
          storage   = EvqTemporary;
          centroid  = false;
          smooth    = false;
          flat      = false;
          nopersp   = false;
          patch     = false;
          sample    = false;
          coherent  = false;
          volatil   = false;
          restrict  = false;
          readonly  = false;
          writeonly = false;
          clearLayout();
      }
  
      TStorageQualifier   storage   : 6;
      TPrecisionQualifier precision : 3;
      bool invariant : 1;
      bool centroid  : 1;
      bool smooth    : 1;
      bool flat      : 1;
      bool nopersp   : 1;
      bool patch     : 1;
      bool sample    : 1;
      bool coherent  : 1;
      bool volatil   : 1;
      bool restrict  : 1;
      bool readonly  : 1;
      bool writeonly : 1;
  
      bool isMemory() const
      {
          return coherent || volatil || restrict || readonly || writeonly;
      }
      bool isInterpolation() const
      {
          return flat || smooth || nopersp;
      }
      bool isAuxiliary() const
      {
          return centroid || patch || sample;
      }
  
      bool isPipeInput() const
      {
          switch (storage) {
          case EvqVaryingIn:
          case EvqFragCoord:
          case EvqPointCoord:
          case EvqFace:
          case EvqVertexId:
          case EvqInstanceId:
              return true;
          default:
              return false;
          }
      }
  
      bool isPipeOutput() const
      {
          switch (storage) {
          case EvqPosition:
          case EvqPointSize:
          case EvqClipVertex:
          case EvqVaryingOut:
          case EvqFragColor:
          case EvqFragDepth:
              return true;
          default:
              return false;
          }
      }
  
      bool isParamInput() const
      {
          switch (storage) {
          case EvqIn:
          case EvqInOut:
          case EvqConstReadOnly:
              return true;
          default:
              return false;
          }
      }
  
      bool isParamOutput() const
      {
          switch (storage) {
          case EvqOut:
          case EvqInOut:
              return true;
          default:
              return false;
          }
      }
  
      bool isUniformOrBuffer() const
      {
          switch (storage) {
          case EvqUniform:
          case EvqBuffer:
              return true;
          default:
              return false;
          }
      }
  
      bool isIo() const
      {
          switch (storage) {
          case EvqUniform:
          case EvqBuffer:
          case EvqVaryingIn:
          case EvqFragCoord:
          case EvqPointCoord:
          case EvqFace:
          case EvqVertexId:
          case EvqInstanceId:
          case EvqPosition:
          case EvqPointSize:
          case EvqClipVertex:
          case EvqVaryingOut:
          case EvqFragColor:
          case EvqFragDepth:
              return true;
          default:
              return false;
          }
      }
  
      // True if this type of IO is supposed to be arrayed with extra level for per-vertex data
      bool isArrayedIo(EShLanguage language) const
      {
          switch (language) {
          case EShLangGeometry:
              return isPipeInput();
          case EShLangTessControl:
              return ! patch && (isPipeInput() || isPipeOutput());
          case EShLangTessEvaluation:
              return ! patch && isPipeInput();
          default:
              return false;
          }
      }
  
      // Implementing an embedded layout-qualifier class here, since C++ can't have a real class bitfield
      void clearLayout()
      {
          layoutMatrix = ElmNone;
          layoutPacking = ElpNone;
          layoutOffset = -1;
          layoutAlign = -1;
  
          layoutLocation = layoutLocationEnd;
          layoutComponent = layoutComponentEnd;
          layoutSet = layoutSetEnd;
          layoutBinding = layoutBindingEnd;
          layoutIndex = layoutIndexEnd;
  
          layoutStream = layoutStreamEnd;
  
          layoutXfbBuffer = layoutXfbBufferEnd;
          layoutXfbStride = layoutXfbStrideEnd;
          layoutXfbOffset = layoutXfbOffsetEnd;
  
          layoutFormat = ElfNone;
      }
      bool hasLayout() const
      {
          return hasUniformLayout() || 
                 hasAnyLocation() ||
                 hasBinding() ||
                 hasStream() ||
                 hasXfb() ||
                 hasFormat();
      }
      TLayoutMatrix  layoutMatrix  : 3;
      TLayoutPacking layoutPacking : 4;
      int layoutOffset;
      int layoutAlign;
  
                   unsigned int layoutLocation         : 7;
      static const unsigned int layoutLocationEnd =   0x3F;
  
                   unsigned int layoutComponent        : 3;
      static const unsigned int layoutComponentEnd =     4;
  
                   unsigned int layoutSet              : 7;
      static const unsigned int layoutSetEnd      =   0x3F;
  
                   unsigned int layoutBinding          : 8;
      static const unsigned int layoutBindingEnd =    0xFF;
  
                   unsigned int layoutIndex           :  8;
      static const unsigned int layoutIndexEnd =      0xFF;
  
                   unsigned int layoutStream           : 8;
      static const unsigned int layoutStreamEnd =     0xFF;
  
                   unsigned int layoutXfbBuffer        : 4;
      static const unsigned int layoutXfbBufferEnd =   0xF;
  
                   unsigned int layoutXfbStride       : 10;
      static const unsigned int layoutXfbStrideEnd = 0x3FF;
  
                   unsigned int layoutXfbOffset       : 10;
      static const unsigned int layoutXfbOffsetEnd = 0x3FF;
  
      TLayoutFormat layoutFormat                      :  8;
  
      bool hasUniformLayout() const
      {
          return hasMatrix() ||
                 hasPacking() ||
                 hasOffset() ||
                 hasBinding() ||
                 hasAlign();
      }
      bool hasMatrix() const
      {
          return layoutMatrix != ElmNone;
      }
      bool hasPacking() const
      {
          return layoutPacking != ElpNone;
      }
      bool hasOffset() const
      {
          return layoutOffset != -1;
      }
      bool hasAlign() const
      {
          return layoutAlign != -1;
      }
      bool hasAnyLocation() const
      {
          return hasLocation() ||
                 hasComponent() ||
                 hasIndex();
      }
      bool hasLocation() const
      {
          return layoutLocation  != layoutLocationEnd;
      }
      bool hasComponent() const
      {
          return layoutComponent != layoutComponentEnd;
      }
      bool hasIndex() const
      {
          return layoutIndex != layoutIndexEnd;
      }
      bool hasSet() const
      {
          return layoutSet != layoutSetEnd;
      }
      bool hasBinding() const
      {
          return layoutBinding != layoutBindingEnd;
      }
      bool hasStream() const
      {
          return layoutStream != layoutStreamEnd;
      }
      bool hasFormat() const
      {
          return layoutFormat != ElfNone;
      }
      bool hasXfb() const
      {
          return hasXfbBuffer() ||
                 hasXfbStride() ||
                 hasXfbOffset();
      }
      bool hasXfbBuffer() const
      {
          return layoutXfbBuffer != layoutXfbBufferEnd;
      }
      bool hasXfbStride() const
      {
          return layoutXfbStride != layoutXfbStrideEnd;
      }
      bool hasXfbOffset() const
      {
          return layoutXfbOffset != layoutXfbOffsetEnd;
      }
      static const char* getLayoutPackingString(TLayoutPacking packing)
      {
          switch (packing) {
          case ElpPacked:   return "packed";
          case ElpShared:   return "shared";
          case ElpStd140:   return "std140";
          case ElpStd430:   return "std430";
          default:          return "none";
          }
      }
      static const char* getLayoutMatrixString(TLayoutMatrix m)
      {
          switch (m) {
          case ElmColumnMajor: return "column_major";
          case ElmRowMajor:    return "row_major";
          default:             return "none";
          }
      }
      static const char* getLayoutFormatString(TLayoutFormat f)
      {
          switch (f) {
          case ElfRgba32f:      return "rgba32f";
          case ElfRgba16f:      return "rgba16f";
          case ElfRg32f:        return "rg32f";
          case ElfRg16f:        return "rg16f";
          case ElfR11fG11fB10f: return "r11f_g11f_b10f";
          case ElfR32f:         return "r32f";
          case ElfR16f:         return "r16f";
          case ElfRgba16:       return "rgba16";
          case ElfRgb10A2:      return "rgb10_a2";
          case ElfRgba8:        return "rgba8";
          case ElfRg16:         return "rg16";
          case ElfRg8:          return "rg8";
          case ElfR16:          return "r16";
          case ElfR8:           return "r8";
          case ElfRgba16Snorm:  return "rgba16_snorm";
          case ElfRgba8Snorm:   return "rgba8_snorm";
          case ElfRg16Snorm:    return "rg16_snorm";
          case ElfRg8Snorm:     return "rg8_snorm";
          case ElfR16Snorm:     return "r16_snorm";
          case ElfR8Snorm:      return "r8_snorm";
  
          case ElfRgba32i:      return "rgba32i";
          case ElfRgba16i:      return "rgba16i";
          case ElfRgba8i:       return "rgba8i";
          case ElfRg32i:        return "rg32i";
          case ElfRg16i:        return "rg16i";
          case ElfRg8i:         return "rg8i";
          case ElfR32i:         return "r32i";
          case ElfR16i:         return "r16i";
          case ElfR8i:          return "r8i";
  
          case ElfRgba32ui:     return "rgba32ui";
          case ElfRgba16ui:     return "rgba16ui";
          case ElfRgba8ui:      return "rgba8ui";
          case ElfRg32ui:       return "rg32ui";
          case ElfRg16ui:       return "rg16ui";
          case ElfRg8ui:        return "rg8ui";
          case ElfR32ui:        return "r32ui";
          case ElfR16ui:        return "r16ui";
          case ElfR8ui:         return "r8ui";
          default:              return "none";
          }
      }
      static const char* getLayoutDepthString(TLayoutDepth d)
      {
          switch (d) {
          case EldAny:       return "depth_any";
          case EldGreater:   return "depth_greater";
          case EldLess:      return "depth_less";
          case EldUnchanged: return "depth_unchanged";
          default:           return "none";
          }
      }
      static const char* getGeometryString(TLayoutGeometry geometry)
      {
          switch (geometry) {
          case ElgPoints:             return "points";
          case ElgLines:              return "lines";
          case ElgLinesAdjacency:     return "lines_adjacency";
          case ElgLineStrip:          return "line_strip";
          case ElgTriangles:          return "triangles";
          case ElgTrianglesAdjacency: return "triangles_adjacency";
          case ElgTriangleStrip:      return "triangle_strip";
          case ElgQuads:              return "quads";
          case ElgIsolines:           return "isolines";
          default:                    return "none";
          }
      }
      static const char* getVertexSpacingString(TVertexSpacing spacing)
      {
          switch (spacing) {
          case EvsEqual:              return "equal_spacing";
          case EvsFractionalEven:     return "fractional_even_spacing";
          case EvsFractionalOdd:      return "fractional_odd_spacing";
          default:                    return "none";
          }
      }
      static const char* getVertexOrderString(TVertexOrder order)
      {
          switch (order) {
          case EvoCw:                 return "cw";
          case EvoCcw:                return "ccw";
          default:                    return "none";
          }
      }
      static int mapGeometryToSize(TLayoutGeometry geometry)
      {
          switch (geometry) {
          case ElgPoints:             return 1;
          case ElgLines:              return 2;
          case ElgLinesAdjacency:     return 4;
          case ElgTriangles:          return 3;
          case ElgTrianglesAdjacency: return 6;
          default:                    return 0;
          }
      }
  };
  
  // Qualifiers that don't need to be keep per object.  They have shader scope, not object scope.
  // So, they will not be part of TType, TQualifier, etc.
  struct TShaderQualifiers {
      TLayoutGeometry geometry; // geometry/tessellation shader in/out primitives
      bool pixelCenterInteger;  // fragment shader
      bool originUpperLeft;     // fragment shader
      int invocations;          // 0 means no declaration
      int vertices;             // both for tessellation "vertices" and geometry "max_vertices"
      TVertexSpacing spacing;
      TVertexOrder order;
      bool pointMode;
      int localSize[3];         // compute shader
      bool earlyFragmentTests;  // fragment input
      TLayoutDepth layoutDepth;
  
      void init()
      {
          geometry = ElgNone;
          originUpperLeft = false;
          pixelCenterInteger = false;
          invocations = 0;        // 0 means no declaration
          vertices = 0;
          spacing = EvsNone;
          order = EvoNone;
          pointMode = false;
          localSize[0] = 1;
          localSize[1] = 1;
          localSize[2] = 1;
          earlyFragmentTests = false;
          layoutDepth = EldNone;
      }
  
      // Merge in characteristics from the 'src' qualifier.  They can override when
      // set, but never erase when not set.
      void merge(const TShaderQualifiers& src)
      {
          if (src.geometry != ElgNone)
              geometry = src.geometry;
          if (src.pixelCenterInteger)
              pixelCenterInteger = src.pixelCenterInteger;
          if (src.originUpperLeft)
              originUpperLeft = src.originUpperLeft;
          if (src.invocations != 0)
              invocations = src.invocations;
          if (src.vertices != 0)
              vertices = src.vertices;
          if (src.spacing != EvsNone)
              spacing = src.spacing;
          if (src.order != EvoNone)
              order = src.order;
          if (src.pointMode)
              pointMode = true;
          for (int i = 0; i < 3; ++i) {
              if (src.localSize[i] > 1)
                  localSize[i] = src.localSize[i];
          }
          if (src.earlyFragmentTests)
              earlyFragmentTests = true;
          if (src.layoutDepth)
              layoutDepth = src.layoutDepth;
      }
  };
  
  //
  // TPublicType is just temporarily used while parsing and not quite the same
  // information kept per node in TType.  Due to the bison stack, it can't have
  // types that it thinks have non-trivial constructors.  It should
  // just be used while recognizing the grammar, not anything else.
  // Once enough is known about the situation, the proper information
  // moved into a TType, or the parse context, etc.
  //
  class TPublicType {
  public:
      TBasicType basicType;
      TSampler sampler;
      TQualifier qualifier;
      TShaderQualifiers shaderQualifiers;
      int vectorSize : 4;
      int matrixCols : 4;
      int matrixRows : 4;
      TArraySizes* arraySizes;
      const TType* userDef;
      TSourceLoc loc;
  
      void initType(TSourceLoc l)
      {
          basicType = EbtVoid;
          vectorSize = 1;
          matrixRows = 0;
          matrixCols = 0;
          arraySizes = 0;
          userDef = 0;
          loc = l;
      }
  
      void initQualifiers(bool global = false)
      {
          qualifier.clear();
          if (global)
              qualifier.storage = EvqGlobal;
      }
  
      void init(TSourceLoc loc, bool global = false)
      {
          initType(loc);
          sampler.clear();
          initQualifiers(global);
          shaderQualifiers.init();
      }
  
      void setVector(int s)
      {
          matrixRows = 0;
          matrixCols = 0;
          vectorSize = s;
      }
  
      void setMatrix(int c, int r)
      {
          matrixRows = r;
          matrixCols = c;
          vectorSize = 0;
      }
  
      bool isScalar() const
      {
          return matrixCols == 0 && vectorSize == 1 && arraySizes == 0 && userDef == 0;
      }
  
      bool isImage() const
      {
          return basicType == EbtSampler && sampler.image;
      }
  };
  
  typedef std::map<TTypeList*, TTypeList*> TStructureMap;
  typedef std::map<TTypeList*, TTypeList*>::const_iterator TStructureMapIterator;
  
  //
  // Base class for things that have a type.
  //
  class TType {
  public:
      POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator())
  
      // for "empty" type (no args) or simple scalar/vector/matrix
      explicit TType(TBasicType t = EbtVoid, TStorageQualifier q = EvqTemporary, int vs = 1, int mc = 0, int mr = 0) :
                              basicType(t), vectorSize(vs), matrixCols(mc), matrixRows(mr), arraySizes(0),
                              structure(0), fieldName(0), typeName(0)
                              {
                                  sampler.clear();
                                  qualifier.clear();
                                  qualifier.storage = q;
                              }
      // for explicit precision qualifier
      TType(TBasicType t, TStorageQualifier q, TPrecisionQualifier p, int vs = 1, int mc = 0, int mr = 0) :
                              basicType(t), vectorSize(vs), matrixCols(mc), matrixRows(mr), arraySizes(0),
                              structure(0), fieldName(0), typeName(0)
                              {
                                  sampler.clear();
                                  qualifier.clear();
                                  qualifier.storage = q;
                                  qualifier.precision = p;
                                  assert(p >= 0 && p <= EpqHigh);
                              }
      // for turning a TPublicType into a TType
      explicit TType(const TPublicType& p) :
                              basicType(p.basicType), vectorSize(p.vectorSize), matrixCols(p.matrixCols), matrixRows(p.matrixRows), arraySizes(p.arraySizes),
                              structure(0), fieldName(0), typeName(0)
                              {
                                  if (basicType == EbtSampler)
                                      sampler = p.sampler;
                                  else
                                      sampler.clear();
                                  qualifier = p.qualifier;
                                  if (p.userDef) {
                                      structure = p.userDef->getWritableStruct();  // public type is short-lived; there are no sharing issues
                                      typeName = NewPoolTString(p.userDef->getTypeName().c_str());
                                  }
                              }
      // to efficiently make a dereferenced type
      // without ever duplicating the outer structure that will be thrown away
      // and using only shallow copy
      TType(const TType& type, int derefIndex, bool rowMajor = false)
                              {
                                  if (! type.isArray() && (type.basicType == EbtStruct || type.basicType == EbtBlock)) {
                                      // do a structure dereference
                                      const TTypeList& memberList = *type.getStruct();
                                      shallowCopy(*memberList[derefIndex].type);
                                      return;
                                  } else {
                                      // do an array/vector/matrix dereference
                                      shallowCopy(type);
                                      dereference(rowMajor);
                                  }
                              }
      // for making structures, ...
      TType(TTypeList* userDef, const TString& n) :
                              basicType(EbtStruct), vectorSize(1), matrixCols(0), matrixRows(0),
                              arraySizes(0), structure(userDef), fieldName(0)
                              {
                                  sampler.clear();
                                  qualifier.clear();
                                  typeName = NewPoolTString(n.c_str());
                              }
      // For interface blocks
      TType(TTypeList* userDef, const TString& n, const TQualifier& q) : 
                              basicType(EbtBlock), vectorSize(1), matrixCols(0), matrixRows(0),
                              qualifier(q), arraySizes(0), structure(userDef), fieldName(0)
                              {
                                  sampler.clear();
                                  typeName = NewPoolTString(n.c_str());
                              }
      virtual ~TType() {}
      
      // Not for use across pool pops; it will cause multiple instances of TType to point to the same information.
      // This only works if that information (like a structure's list of types) does not change and 
      // the instances are sharing the same pool. 
      void shallowCopy(const TType& copyOf)
      {
          basicType = copyOf.basicType;
          sampler = copyOf.sampler;
          qualifier = copyOf.qualifier;
          vectorSize = copyOf.vectorSize;
          matrixCols = copyOf.matrixCols;
          matrixRows = copyOf.matrixRows;
          arraySizes = copyOf.arraySizes;
          structure = copyOf.structure;
          fieldName = copyOf.fieldName;
          typeName = copyOf.typeName;
      }
  
      void deepCopy(const TType& copyOf)
      {
          shallowCopy(copyOf);
  
          if (copyOf.arraySizes) {
              arraySizes = new TArraySizes;
              *arraySizes = *copyOf.arraySizes;
          }
  
          if (copyOf.structure) {
              structure = new TTypeList;
              TStructureMapIterator iter;
              for (unsigned int i = 0; i < copyOf.structure->size(); ++i) {
                  TTypeLoc typeLoc;
                  typeLoc.loc = (*copyOf.structure)[i].loc;
                  typeLoc.type = new TType();
                  typeLoc.type->deepCopy(*(*copyOf.structure)[i].type);
                  structure->push_back(typeLoc);
              }
          }
  
          if (copyOf.fieldName)
              fieldName = NewPoolTString(copyOf.fieldName->c_str());
          if (copyOf.typeName)
              typeName = NewPoolTString(copyOf.typeName->c_str());
      }
      
      TType* clone()
      {
          TType *newType = new TType();
          newType->deepCopy(*this);
  
          return newType;
      }
  
      // Merge type from parent, where a parentType is at the beginning of a declaration,
      // establishing some characteristics for all subsequent names, while this type
      // is on the individual names.
      void mergeType(const TPublicType& parentType)
      {
          // arrayness is currently the only child aspect that has to be preserved
          basicType = parentType.basicType;
          vectorSize = parentType.vectorSize;
          matrixCols = parentType.matrixCols;
          matrixRows = parentType.matrixRows;
          qualifier = parentType.qualifier;
          sampler = parentType.sampler;
          if (parentType.arraySizes)
              setArraySizes(parentType.arraySizes);
          if (parentType.userDef) {
              structure = parentType.userDef->getWritableStruct();
              setTypeName(parentType.userDef->getTypeName());
          }
      }
  
      virtual void dereference(bool rowMajor = false)
      {
          if (arraySizes)
              arraySizes = 0;
          else if (matrixCols > 0) {
              if (rowMajor)
                  vectorSize = matrixCols;
              else
                  vectorSize = matrixRows;
              matrixCols = 0;
              matrixRows = 0;
          } else if (vectorSize > 1)
              vectorSize = 1;
      }
  
      virtual void hideMember() { basicType = EbtVoid; vectorSize = 1; }
      virtual bool hiddenMember() const { return basicType == EbtVoid; }
  
      virtual void setTypeName(const TString& n) { typeName = NewPoolTString(n.c_str()); }
      virtual void setFieldName(const TString& n) { fieldName = NewPoolTString(n.c_str()); }
      virtual const TString& getTypeName() const
      {
          assert(typeName);
          return *typeName;
      }
  
      virtual const TString& getFieldName() const
      {
          assert(fieldName);
          return *fieldName;
      }
  
      virtual TBasicType getBasicType() const { return basicType; }
      virtual const TSampler& getSampler() const { return sampler; }
  
      virtual       TQualifier& getQualifier()       { return qualifier; }
      virtual const TQualifier& getQualifier() const { return qualifier; }
  
      virtual int getVectorSize() const { return vectorSize; }
      virtual int getMatrixCols() const { return matrixCols; }
      virtual int getMatrixRows() const { return matrixRows; }
      virtual int getArraySize()  const { return arraySizes->sizes.front(); }
      virtual bool isArrayOfArrays() const { return arraySizes && arraySizes->isArrayOfArrays(); }
      virtual int getImplicitArraySize () const { return arraySizes->implicitArraySize; }
  
      virtual bool isScalar() const { return vectorSize == 1 && ! isStruct() && ! isArray(); }
      virtual bool isVector() const { return vectorSize > 1; }
      virtual bool isMatrix() const { return matrixCols ? true : false; }
      virtual bool isArray()  const { return arraySizes != 0; }
      virtual bool isImplicitlySizedArray() const { return isArray() && ! getArraySize() && qualifier.storage != EvqBuffer; }
      virtual bool isExplicitlySizedArray() const { return isArray() && getArraySize(); }
      virtual bool isRuntimeSizedArray() const { return isArray() && ! getArraySize() && qualifier.storage == EvqBuffer; }
      virtual bool isStruct() const { return structure != 0; }
      virtual bool isImage() const { return basicType == EbtSampler && getSampler().image; }
  
      // Recursively checks if the type contains the given basic type
      virtual bool containsBasicType(TBasicType checkType) const
      {
          if (basicType == checkType)
              return true;
          if (! structure)
              return false;
          for (unsigned int i = 0; i < structure->size(); ++i) {
              if ((*structure)[i].type->containsBasicType(checkType))
                  return true;
          }
          return false;
      }
  
      // Recursively check the structure for any arrays, needed for some error checks
      virtual bool containsArray() const
      {
          if (isArray())
              return true;
          if (! structure)
              return false;
          for (unsigned int i = 0; i < structure->size(); ++i) {
              if ((*structure)[i].type->containsArray())
                  return true;
          }
          return false;
      }
  
      // Check the structure for any structures, needed for some error checks
      virtual bool containsStructure() const
      {
          if (! structure)
              return false;
          for (unsigned int i = 0; i < structure->size(); ++i) {
              if ((*structure)[i].type->structure)
                  return true;
          }
          return false;
      }
  
      // Recursively check the structure for any implicitly-sized arrays, needed for triggering a copyUp().
      virtual bool containsImplicitlySizedArray() const
      {
          if (isImplicitlySizedArray())
              return true;
          if (! structure)
              return false;
          for (unsigned int i = 0; i < structure->size(); ++i) {
              if ((*structure)[i].type->containsImplicitlySizedArray())
                  return true;
          }
          return false;
      }
  
      // Array editing methods.  Array descriptors can be shared across
      // type instances.  This allows all uses of the same array
      // to be updated at once.  E.g., all nodes can be explicitly sized
      // by tracking and correcting one implicit size.  Or, all nodes
      // can get the explicit size on a redeclaration that gives size.
      //
      // N.B.:  Don't share with the shared symbol tables (symbols are
      // marked as isReadOnly().  Such symbols with arrays that will be
      // edited need to copyUp() on first use, so that 
      // A) the edits don't effect the shared symbol table, and
      // B) the edits are shared across all users.
      void updateArraySizes(const TType& type)
      {
          // For when we may already be sharing existing array descriptors,
          // keeping the pointers the same, just updating the contents.
          *arraySizes = *type.arraySizes;
      }
      void setArraySizes(TArraySizes* s)
      {
          // For setting a fresh new set of array sizes, not yet worrying about sharing.
          arraySizes = new TArraySizes;
          *arraySizes = *s;
      }
      void setArraySizes(const TType& type) { setArraySizes(type.arraySizes); }
      void changeArraySize(int s) { arraySizes->sizes.front() = s; }
      void setImplicitArraySize (int s) { arraySizes->implicitArraySize = s; }
  
      // Recursively make the implicit array size the explicit array size, through the type tree.
      void adoptImplicitArraySizes()
      {
          if (isImplicitlySizedArray())
              changeArraySize(getImplicitArraySize());
          if (isStruct()) {
              for (int i = 0; i < (int)structure->size(); ++i)
                  (*structure)[i].type->adoptImplicitArraySizes();
          }
      }
  
      const char* getBasicString() const 
      {
          return TType::getBasicString(basicType);
      }
      
      static const char* getBasicString(TBasicType t)
      {
          switch (t) {
          case EbtVoid:              return "void";
          case EbtFloat:             return "float";
          case EbtDouble:            return "double";
          case EbtInt:               return "int";
          case EbtUint:              return "uint";
          case EbtBool:              return "bool";
          case EbtAtomicUint:        return "atomic_uint";
          case EbtSampler:           return "sampler/image";
          case EbtStruct:            return "structure";
          case EbtBlock:             return "block";
          default:                   return "unknown type";
          }
      }
  
      TString getCompleteString() const
      {
          const int maxSize = GlslangMaxTypeLength;
          char buf[maxSize];
          char* p = &buf[0];
          char* end = &buf[maxSize];
  
          if (qualifier.hasLayout()) {
              // To reduce noise, skip this if the only layout is an xfb_buffer
              // with no triggering xfb_offset.
              TQualifier noXfbBuffer = qualifier;
              noXfbBuffer.layoutXfbBuffer = TQualifier::layoutXfbBufferEnd;
              if (noXfbBuffer.hasLayout()) {
                  p += snprintf(p, end - p, "layout(");
                  if (qualifier.hasAnyLocation()) {
                      p += snprintf(p, end - p, "location=%d ", qualifier.layoutLocation);
                      if (qualifier.hasComponent())
                          p += snprintf(p, end - p, "component=%d ", qualifier.layoutComponent);
                      if (qualifier.hasIndex())
                          p += snprintf(p, end - p, "index=%d ", qualifier.layoutIndex);
                  }
                  if (qualifier.hasSet())
                      p += snprintf(p, end - p, "set=%d ", qualifier.layoutSet);
                  if (qualifier.hasBinding())
                      p += snprintf(p, end - p, "binding=%d ", qualifier.layoutBinding);
                  if (qualifier.hasStream())
                      p += snprintf(p, end - p, "stream=%d ", qualifier.layoutStream);
                  if (qualifier.hasMatrix())
                      p += snprintf(p, end - p, "%s ", TQualifier::getLayoutMatrixString(qualifier.layoutMatrix));
                  if (qualifier.hasPacking())
                      p += snprintf(p, end - p, "%s ", TQualifier::getLayoutPackingString(qualifier.layoutPacking));
                  if (qualifier.hasOffset())
                      p += snprintf(p, end - p, "offset=%d ", qualifier.layoutOffset);
                  if (qualifier.hasAlign())
                      p += snprintf(p, end - p, "align=%d ", qualifier.layoutAlign);
                  if (qualifier.hasFormat())
                      p += snprintf(p, end - p, "%s ", TQualifier::getLayoutFormatString(qualifier.layoutFormat));
                  if (qualifier.hasXfbBuffer() && qualifier.hasXfbOffset())
                      p += snprintf(p, end - p, "xfb_buffer=%d ", qualifier.layoutXfbBuffer);
                  if (qualifier.hasXfbOffset())
                      p += snprintf(p, end - p, "xfb_offset=%d ", qualifier.layoutXfbOffset);
                  if (qualifier.hasXfbStride())
                      p += snprintf(p, end - p, "xfb_stride=%d ", qualifier.layoutXfbStride);
                  p += snprintf(p, end - p, ") ");
              }
          }
  
          if (qualifier.invariant)
              p += snprintf(p, end - p, "invariant ");
          if (qualifier.centroid)
              p += snprintf(p, end - p, "centroid ");
          if (qualifier.smooth)
              p += snprintf(p, end - p, "smooth ");
          if (qualifier.flat)
              p += snprintf(p, end - p, "flat ");
          if (qualifier.nopersp)
              p += snprintf(p, end - p, "noperspective ");
          if (qualifier.patch)
              p += snprintf(p, end - p, "patch ");
          if (qualifier.sample)
              p += snprintf(p, end - p, "sample ");
          if (qualifier.coherent)
              p += snprintf(p, end - p, "coherent ");
          if (qualifier.volatil)
              p += snprintf(p, end - p, "volatile ");
          if (qualifier.restrict)
              p += snprintf(p, end - p, "restrict ");
          if (qualifier.readonly)
              p += snprintf(p, end - p, "readonly ");
          if (qualifier.writeonly)
              p += snprintf(p, end - p, "writeonly ");
          p += snprintf(p, end - p, "%s ", getStorageQualifierString());
          if (arraySizes) {
              if (arraySizes->sizes.front() == 0)
                  p += snprintf(p, end - p, "implicitly-sized array of ");
              else
                  p += snprintf(p, end - p, "%d-element array of ", arraySizes->sizes.front());
          }
          if (qualifier.precision != EpqNone)
              p += snprintf(p, end - p, "%s ", getPrecisionQualifierString());
          if (matrixCols > 0)
              p += snprintf(p, end - p, "%dX%d matrix of ", matrixCols, matrixRows);
          else if (vectorSize > 1)
              p += snprintf(p, end - p, "%d-component vector of ", vectorSize);
  
          *p = 0;
          TString s(buf);
          s.append(getBasicTypeString());
  
          // Add struct/block members
          if (structure) {
              s.append("{");
              for (size_t i = 0; i < structure->size(); ++i) {
                  if (! (*structure)[i].type->hiddenMember()) {
                      s.append((*structure)[i].type->getCompleteString());
                      s.append(" ");
                      s.append((*structure)[i].type->getFieldName());
                      if (i < structure->size() - 1)
                          s.append(", ");
                  }
              }
              s.append("}");
          }
  
          return s;
      }
  
      TString getBasicTypeString() const
      {
          if (basicType == EbtSampler)
              return sampler.getString();
          else
              return getBasicString();
      }
  
      const char* getStorageQualifierString() const { return GetStorageQualifierString(qualifier.storage); }
      const char* getPrecisionQualifierString() const { return GetPrecisionQualifierString(qualifier.precision); }
      const TTypeList* getStruct() const { return structure; }
      TTypeList* getWritableStruct() const { return structure; }  // This should only be used when known to not be sharing with other threads
  
      int computeNumComponents() const
      {
          int components = 0;
  
          if (getBasicType() == EbtStruct || getBasicType() == EbtBlock) {
              for (TTypeList::const_iterator tl = getStruct()->begin(); tl != getStruct()->end(); tl++)
                  components += ((*tl).type)->computeNumComponents();
          } else if (matrixCols)
              components = matrixCols * matrixRows;
          else
              components = vectorSize;
  
          if (isArray()) {
              // this function can only be used in paths that have a known array size
              assert(isExplicitlySizedArray());
              components *= getArraySize();
          }
  
          return components;
      }
  
      // append this type's mangled name to the passed in 'name'
      void appendMangledName(TString& name)
      {
          buildMangledName(name);
          name += ';' ;
      }
  
      // Do two structure types match?  They could be declared independently,
      // in different places, but still might satisfy the definition of matching.
      // From the spec:
      //
      // "Structures must have the same name, sequence of type names, and 
      //  type definitions, and member names to be considered the same type. 
      //  This rule applies recursively for nested or embedded types."
      //
      bool sameStructType(const TType& right) const
      {
          // Most commonly, they are both 0, or the same pointer to the same actual structure
          if (structure == right.structure)
              return true;
  
          // Both being 0 was caught above, now they both have to be structures of the same number of elements
          if (structure == 0 || right.structure == 0 ||
              structure->size() != right.structure->size())
              return false;
  
          // Structure names have to match
          if (*typeName != *right.typeName)
              return false;
  
          // Compare the names and types of all the members, which have to match
          for (unsigned int i = 0; i < structure->size(); ++i) {
              if ((*structure)[i].type->getFieldName() != (*right.structure)[i].type->getFieldName())
                  return false;
  
              if (*(*structure)[i].type != *(*right.structure)[i].type)
                  return false;
          }
  
          return true;
      }
  
      // See if two types match, in all aspects except arrayness
      bool sameElementType(const TType& right) const
      {
          return basicType == right.basicType && sameElementShape(right);
      }
  
      // See if two type's arrayness match
      bool sameArrayness(const TType& right) const
      {
          return ((arraySizes == 0 && right.arraySizes == 0) ||
                  (arraySizes && right.arraySizes && arraySizes->sizes == right.arraySizes->sizes));
      }
  
      // See if two type's elements match in all ways except basic type
      bool sameElementShape(const TType& right) const
      {
          return    sampler == right.sampler    &&
                 vectorSize == right.vectorSize &&
                 matrixCols == right.matrixCols &&
                 matrixRows == right.matrixRows &&
                 sameStructType(right);
      }
  
      // See if two types match in all ways (just the actual type, not qualification)
      bool operator==(const TType& right) const
      {
          return sameElementType(right) && sameArrayness(right);
      }
  
      bool operator!=(const TType& right) const
      {
          return ! operator==(right);
      }
  
  protected:
      // Require consumer to pick between deep copy and shallow copy.
      TType(const TType& type);
      TType& operator=(const TType& type);
  
      void buildMangledName(TString&);
  
      TBasicType basicType : 8;
      int vectorSize       : 4;
      int matrixCols       : 4;
      int matrixRows       : 4;
      TSampler sampler;
      TQualifier qualifier;
  
      TArraySizes* arraySizes;    // 0 unless this is an array; can be shared across types
      TTypeList* structure;       // 0 unless this is a struct; can be shared across types
      TString *fieldName;         // for structure field names
      TString *typeName;          // for structure type name
  };
  
  } // end namespace glslang
  
  #endif // _TYPES_INCLUDED_
  