Enforce fragment precision qualifier requirement
TRAC #12156
The fragment shader has no default precision qualifier for floating-point types,
so compilation should fail when it's not set explicitly globally or per declaration.
Signed-off-by: Shannon Woods
Signed-off-by: Daniel Koch

Author:    Nicolas Capens

git-svn-id: https://angleproject.googlecode.com/svn/trunk@293 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/compiler/BaseTypes.h b/src/compiler/BaseTypes.h
index 93df646..cb646fc 100644
--- a/src/compiler/BaseTypes.h
+++ b/src/compiler/BaseTypes.h
@@ -12,9 +12,11 @@
 //
 enum TPrecision
 {
-    EbpHigh,
-    EbpMedium,
+    // These need to be kept sorted
+    EbpUndefined,
     EbpLow,
+    EbpMedium,
+    EbpHigh,
 };
 
 __inline const char* getPrecisionString(TPrecision p)
@@ -24,7 +26,7 @@
     case EbpHigh:		return "highp";		break;
     case EbpMedium:		return "mediump";	break;
     case EbpLow:		return "lowp";		break;
-    default:			return "unknown precision";
+    default:			return "mediump";   break;   // Safest fallback
     }
 }
 
diff --git a/src/compiler/Initialize.cpp b/src/compiler/Initialize.cpp
index 306ac09..ab61528 100644
--- a/src/compiler/Initialize.cpp
+++ b/src/compiler/Initialize.cpp
@@ -23,6 +23,8 @@
     TString BuiltInFunctionsVertex;
     TString BuiltInFunctionsFragment;
     TString StandardUniforms;
+    TString VertexDefaultPrecision;
+    TString FragmentDefaultPrecision;
 
     {
         //============================================================================
@@ -427,19 +429,47 @@
         // Depth range in window coordinates
         //
         s.append(TString("struct gl_DepthRangeParameters {"));
-        s.append(TString("    float near;"));        // n       // FIXME: highp
-        s.append(TString("    float far;"));         // f       // FIXME: highp
-        s.append(TString("    float diff;"));        // f - n   // FIXME: highp
+        s.append(TString("    highp float near;"));        // n
+        s.append(TString("    highp float far;"));         // f
+        s.append(TString("    highp float diff;"));        // f - n
         s.append(TString("};"));
         s.append(TString("uniform gl_DepthRangeParameters gl_DepthRange;"));
 
         s.append(TString("\n"));
     }
+    {
+        //============================================================================
+        //
+        // Default precision for vertex shaders.
+        //
+        //============================================================================
 
+        TString& s = VertexDefaultPrecision;
+
+        s.append(TString("precision highp int;"));
+        s.append(TString("precision highp float;"));
+        s.append(TString("\n"));
+    }
+    {
+        //============================================================================
+        //
+        // Default precision for fragment shaders.
+        //
+        //============================================================================
+        
+        TString& s = FragmentDefaultPrecision;
+        
+        s.append(TString("precision mediump int;"));
+        // No default precision for float in fragment shaders
+        s.append(TString("\n"));
+    }
+
+    builtInStrings[EShLangFragment].push_back(FragmentDefaultPrecision);
     builtInStrings[EShLangFragment].push_back(BuiltInFunctions.c_str());
     builtInStrings[EShLangFragment].push_back(BuiltInFunctionsFragment);
     builtInStrings[EShLangFragment].push_back(StandardUniforms);
-
+    
+    builtInStrings[EShLangVertex].push_back(VertexDefaultPrecision);
     builtInStrings[EShLangVertex].push_back(BuiltInFunctions);
     builtInStrings[EShLangVertex].push_back(BuiltInFunctionsVertex);
     builtInStrings[EShLangVertex].push_back(StandardUniforms);
@@ -489,18 +519,18 @@
     switch(language) {
 
     case EShLangFragment: {
-            symbolTable.insert(*new TVariable(NewPoolTString("gl_FragCoord"),                   TType(EbtFloat, EvqFragCoord,   4)));   // FIXME: mediump
-            symbolTable.insert(*new TVariable(NewPoolTString("gl_FrontFacing"),                 TType(EbtBool,  EvqFrontFacing, 1)));
-            symbolTable.insert(*new TVariable(NewPoolTString("gl_FragColor"),                   TType(EbtFloat, EvqFragColor,   4)));   // FIXME: mediump
-            symbolTable.insert(*new TVariable(NewPoolTString("gl_FragData[gl_MaxDrawBuffers]"), TType(EbtFloat, EvqFragData,    4)));   // FIXME: mediump
-            symbolTable.insert(*new TVariable(NewPoolTString("gl_PointCoord"),                  TType(EbtFloat, EvqPointCoord,  2)));   // FIXME: mediump
+            symbolTable.insert(*new TVariable(NewPoolTString("gl_FragCoord"),                   TType(EbtFloat, EbpMedium, EvqFragCoord,   4)));
+            symbolTable.insert(*new TVariable(NewPoolTString("gl_FrontFacing"),                 TType(EbtBool,  EbpUndefined, EvqFrontFacing, 1)));
+            symbolTable.insert(*new TVariable(NewPoolTString("gl_FragColor"),                   TType(EbtFloat, EbpMedium, EvqFragColor,   4)));
+            symbolTable.insert(*new TVariable(NewPoolTString("gl_FragData[gl_MaxDrawBuffers]"), TType(EbtFloat, EbpMedium, EvqFragData,    4)));
+            symbolTable.insert(*new TVariable(NewPoolTString("gl_PointCoord"),                  TType(EbtFloat, EbpMedium, EvqPointCoord,  2)));
 
         }
         break;
 
     case EShLangVertex:
-        symbolTable.insert(*new TVariable(NewPoolTString("gl_Position"),    TType(EbtFloat, EvqPosition,    4)));   // FIXME: highp
-        symbolTable.insert(*new TVariable(NewPoolTString("gl_PointSize"),   TType(EbtFloat, EvqPointSize,   1)));   // FIXME: mediump
+        symbolTable.insert(*new TVariable(NewPoolTString("gl_Position"),    TType(EbtFloat, EbpHigh, EvqPosition,    4)));
+        symbolTable.insert(*new TVariable(NewPoolTString("gl_PointSize"),   TType(EbtFloat, EbpMedium, EvqPointSize,   1)));
         break;
     default: break;
     }
@@ -587,7 +617,7 @@
 
     case EShLangFragment: {
             // Set up gl_FragData.  The array size.
-            TType fragData(EbtFloat, EvqFragColor,   4, false, true);
+            TType fragData(EbtFloat, EbpMedium, EvqFragColor,   4, false, true);
             fragData.setArraySize(resources.maxDrawBuffers);
             symbolTable.insert(*new TVariable(NewPoolTString("gl_FragData"),    fragData));
         }
diff --git a/src/compiler/Intermediate.cpp b/src/compiler/Intermediate.cpp
index 4a228f5..f1044b4 100644
--- a/src/compiler/Intermediate.cpp
+++ b/src/compiler/Intermediate.cpp
@@ -16,6 +16,15 @@
 
 bool CompareStructure(const TType& leftNodeType, ConstantUnion* rightUnionArray, ConstantUnion* leftUnionArray);
 
+TPrecision GetHighestPrecision( TPrecision left, TPrecision right, TInfoSink& infoSink ){
+    TPrecision highest = left > right ? left : right;
+
+    if (highest == EbpUndefined) {
+        infoSink.info.message(EPrefixInternalError, "Unknown or invalid precision for operands", 0);
+    }
+
+    return highest;
+}
 ////////////////////////////////////////////////////////////////////////////
 //
 // First set of functions are to help build the intermediate representation.
@@ -223,7 +232,8 @@
     }
 
     if (newType != EbtVoid) {
-        child = addConversion(op, TType(newType, EvqTemporary, child->getNominalSize(),
+        child = addConversion(op, TType(newType, child->getPrecision(), EvqTemporary,
+            child->getNominalSize(),
             child->isMatrix(),
             child->isArray()),
             child);
@@ -420,7 +430,7 @@
                 return 0;
         }
 
-        TType type(promoteTo, EvqTemporary, node->getNominalSize(), node->isMatrix(), node->isArray());
+        TType type(promoteTo, node->getPrecision(), EvqTemporary, node->getNominalSize(), node->isMatrix(), node->isArray());
         newNode = new TIntermUnary(newOp, type);
         newNode->setLine(node->getLine());
         newNode->setOperand(node);
@@ -591,7 +601,7 @@
     for (int i = 0; i < fields.num; i++) {
         unionArray = new ConstantUnion[1];
         unionArray->setIConst(fields.offsets[i]);
-        constIntNode = addConstantUnion(unionArray, TType(EbtInt, EvqConst), line);
+        constIntNode = addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConst), line);
         sequenceVector.push_back(constIntNode);
     }
 
@@ -777,6 +787,9 @@
     //
     setType(left->getType());
 
+    TPrecision highestPrecision = GetHighestPrecision(left->getPrecision(), right->getPrecision(), infoSink);
+    getTypePointer()->changePrecision(highestPrecision);
+
     //
     // Array operations.
     //
@@ -789,7 +802,7 @@
             //
             case EOpEqual:
             case EOpNotEqual:
-                setType(TType(EbtBool));
+                setType(TType(EbtBool, EbpUndefined));
                 break;
 
             //
@@ -824,7 +837,7 @@
             case EOpGreaterThan:
             case EOpLessThanEqual:
             case EOpGreaterThanEqual:
-                setType(TType(EbtBool));
+                setType(TType(EbtBool, EbpUndefined));
                 break;
 
             //
@@ -834,7 +847,7 @@
             case EOpLogicalOr:
                 if (left->getBasicType() != EbtBool || right->getBasicType() != EbtBool)
                     return false;
-                setType(TType(EbtBool));
+                setType(TType(EbtBool, EbpUndefined));
                 break;
 
             //
@@ -866,12 +879,12 @@
                     op = EOpVectorTimesMatrix;
                 else {
                     op = EOpMatrixTimesScalar;
-                    setType(TType(type, EvqTemporary, size, true));
+                    setType(TType(type, highestPrecision, EvqTemporary, size, true));
                 }
             } else if (left->isMatrix() && !right->isMatrix()) {
                 if (right->isVector()) {
                     op = EOpMatrixTimesVector;
-                    setType(TType(type, EvqTemporary, size, false));
+                    setType(TType(type, highestPrecision, EvqTemporary, size, false));
                 } else {
                     op = EOpMatrixTimesScalar;
                 }
@@ -882,7 +895,7 @@
                     // leave as component product
                 } else if (left->isVector() || right->isVector()) {
                     op = EOpVectorTimesScalar;
-                    setType(TType(type, EvqTemporary, size, false));
+                    setType(TType(type, highestPrecision, EvqTemporary, size, false));
                 }
             } else {
                 infoSink.info.message(EPrefixInternalError, "Missing elses", getLine());
@@ -911,7 +924,7 @@
                     if (! left->isVector())
                         return false;
                     op = EOpVectorTimesScalarAssign;
-                    setType(TType(type, EvqTemporary, size, false));
+                    setType(TType(type, highestPrecision, EvqTemporary, size, false));
                 }
             } else {
                 infoSink.info.message(EPrefixInternalError, "Missing elses", getLine());
@@ -933,7 +946,7 @@
                 left->isVector() && right->isMatrix() ||
                 left->getBasicType() != right->getBasicType())
                 return false;
-            setType(TType(type, EvqTemporary, size, left->isMatrix() || right->isMatrix()));
+            setType(TType(type, highestPrecision, EvqTemporary, size, left->isMatrix() || right->isMatrix()));
             break;
 
         case EOpEqual:
@@ -946,7 +959,7 @@
                 left->isVector() && right->isMatrix() ||
                 left->getBasicType() != right->getBasicType())
                 return false;
-            setType(TType(EbtBool));
+            setType(TType(EbtBool, EbpUndefined));
             break;
 
         default:
@@ -1192,13 +1205,13 @@
                 assert(objectSize == 1);
                 tempConstArray = new ConstantUnion[1];
                 tempConstArray->setBConst(*unionArray < *rightUnionArray);
-                returnType = TType(EbtBool, EvqConst);
+                returnType = TType(EbtBool, EbpUndefined, EvqConst);
                 break;
             case EOpGreaterThan:
                 assert(objectSize == 1);
                 tempConstArray = new ConstantUnion[1];
                 tempConstArray->setBConst(*unionArray > *rightUnionArray);
-                returnType = TType(EbtBool, EvqConst);
+                returnType = TType(EbtBool, EbpUndefined, EvqConst);
                 break;
             case EOpLessThanEqual:
                 {
@@ -1207,7 +1220,7 @@
                     constant.setBConst(*unionArray > *rightUnionArray);
                     tempConstArray = new ConstantUnion[1];
                     tempConstArray->setBConst(!constant.getBConst());
-                    returnType = TType(EbtBool, EvqConst);
+                    returnType = TType(EbtBool, EbpUndefined, EvqConst);
                     break;
                 }
             case EOpGreaterThanEqual:
@@ -1217,7 +1230,7 @@
                     constant.setBConst(*unionArray < *rightUnionArray);
                     tempConstArray = new ConstantUnion[1];
                     tempConstArray->setBConst(!constant.getBConst());
-                    returnType = TType(EbtBool, EvqConst);
+                    returnType = TType(EbtBool, EbpUndefined, EvqConst);
                     break;
                 }
 
@@ -1242,7 +1255,7 @@
                     tempConstArray->setBConst(false);
                 }
 
-                tempNode = new TIntermConstantUnion(tempConstArray, TType(EbtBool, EvqConst));
+                tempNode = new TIntermConstantUnion(tempConstArray, TType(EbtBool, EbpUndefined, EvqConst));
                 tempNode->setLine(getLine());
 
                 return tempNode;
@@ -1268,7 +1281,7 @@
                     tempConstArray->setBConst(false);
                 }
 
-                tempNode = new TIntermConstantUnion(tempConstArray, TType(EbtBool, EvqConst));
+                tempNode = new TIntermConstantUnion(tempConstArray, TType(EbtBool, EbpUndefined, EvqConst));
                 tempNode->setLine(getLine());
 
                 return tempNode;
@@ -1386,7 +1399,7 @@
 
     const TType& t = node->getType();
 
-    return addConstantUnion(leftUnionArray, TType(promoteTo, t.getQualifier(), t.getNominalSize(), t.isMatrix(), t.isArray()), node->getLine());
+    return addConstantUnion(leftUnionArray, TType(promoteTo, t.getPrecision(), t.getQualifier(), t.getNominalSize(), t.isMatrix(), t.isArray()), node->getLine());
 }
 
 void TIntermAggregate::addToPragmaTable(const TPragmaTable& pTable)
diff --git a/src/compiler/ParseHelper.cpp b/src/compiler/ParseHelper.cpp
index 7141b0c..396e87a 100644
--- a/src/compiler/ParseHelper.cpp
+++ b/src/compiler/ParseHelper.cpp
@@ -225,6 +225,24 @@
             op, left.c_str(), right.c_str());
 }
 
+bool TParseContext::precisionErrorCheck(int line, TPrecision precision, TBasicType type){
+    switch( type ){
+    case EbtFloat:
+        if( precision == EbpUndefined ){
+            error( line, "No precision specified for (float)", "", "" );
+            return true;
+        }
+        break;
+    case EbtInt:
+        if( precision == EbpUndefined ){
+            error( line, "No precision specified (int)", "", "" );
+            return true;
+        }
+        break;
+    }
+    return false;
+}
+
 //
 // Both test and if necessary, spit out an error, to see if the node is really
 // an l-value that can be operated on this way.
diff --git a/src/compiler/ParseHelper.h b/src/compiler/ParseHelper.h
index 995f918..718ff32 100644
--- a/src/compiler/ParseHelper.h
+++ b/src/compiler/ParseHelper.h
@@ -65,6 +65,7 @@
     void assignError(int line, const char* op, TString left, TString right);
     void unaryOpError(int line, const char* op, TString operand);
     void binaryOpError(int line, const char* op, TString left, TString right);
+    bool precisionErrorCheck(int line, TPrecision precision, TBasicType type);
     bool lValueErrorCheck(int line, const char* op, TIntermTyped*);
     bool constErrorCheck(TIntermTyped* node);
     bool integerErrorCheck(TIntermTyped* node, const char* token);
diff --git a/src/compiler/SymbolTable.cpp b/src/compiler/SymbolTable.cpp
index 483204c..142763d 100644
--- a/src/compiler/SymbolTable.cpp
+++ b/src/compiler/SymbolTable.cpp
@@ -75,7 +75,7 @@
 
 void TVariable::dump(TInfoSink& infoSink) const
 {
-    infoSink.debug << getName().c_str() << ": " << type.getQualifierString() << " " << type.getBasicString();
+    infoSink.debug << getName().c_str() << ": " << type.getQualifierString() << " " << type.getPrecisionString() << " " << type.getBasicString();
     if (type.isArray()) {
         infoSink.debug << "[0]";
     }
@@ -208,4 +208,7 @@
     for (unsigned int i = 0; i < copyOf.table.size(); ++i) {
         table.push_back(copyOf.table[i]->clone(remapper));
     }
+    for( unsigned int i = 0; i < copyOf.precisionStack.size(); i++) {
+        precisionStack.push_back( copyOf.precisionStack[i] );
+    }
 }
diff --git a/src/compiler/SymbolTable.h b/src/compiler/SymbolTable.h
index f58cdb8..fa6e64d 100644
--- a/src/compiler/SymbolTable.h
+++ b/src/compiler/SymbolTable.h
@@ -129,7 +129,7 @@
 public:
     TFunction(TOperator o) :
         TSymbol(0),
-        returnType(TType(EbtVoid)),
+        returnType(TType(EbtVoid, EbpUndefined)),
         op(o),
         defined(false) { }
     TFunction(const TString *name, TType& retType, TOperator tOp = EOpNull) : 
@@ -241,6 +241,7 @@
     TSymbolTable(TSymbolTable& symTable)
     {
         table.push_back(symTable.table[0]);
+        precisionStack.push_back( symTable.precisionStack[0] );
         uniqueId = symTable.uniqueId;
     }
 
@@ -263,12 +264,14 @@
     void push()
     { 
         table.push_back(new TSymbolTableLevel);
+        precisionStack.push_back( PrecisionStackLevel() );
     }
 
     void pop()
     { 
         delete table[currentLevel()]; 
         table.pop_back(); 
+        precisionStack.pop_back();
     }
 
     bool insert(TSymbol& symbol)
@@ -299,11 +302,37 @@
     void dump(TInfoSink &infoSink) const;
     void copyTable(const TSymbolTable& copyOf);
 
+    void setDefaultPrecision( TBasicType type, TPrecision prec ){
+        if( type != EbtFloat && type != EbtInt ) return; // Only set default precision for int/float
+        int indexOfLastElement = static_cast<int>(precisionStack.size()) - 1;
+        precisionStack[indexOfLastElement][type] = prec; // Uses map operator [], overwrites the current value
+    }
+
+    // Searches down the precisionStack for a precision qualifier for the specified TBasicType
+    TPrecision getDefaultPrecision( TBasicType type){
+        if( type != EbtFloat && type != EbtInt ) return EbpUndefined;
+        int level = static_cast<int>(precisionStack.size()) - 1;
+        assert( level >= 0); // Just to be safe. Should not happen.
+        PrecisionStackLevel::iterator it;
+        TPrecision prec = EbpUndefined; // If we dont find anything we return this. Should we error check this?
+        while( level >= 0 ){
+            it = precisionStack[level].find( type );
+            if( it != precisionStack[level].end() ){
+                prec = (*it).second;
+                break;
+            }
+            level--;
+        }
+        return prec;
+    }
+
 protected:    
     int currentLevel() const { return static_cast<int>(table.size()) - 1; }
     bool atDynamicBuiltInLevel() { return table.size() == 2; }
 
     std::vector<TSymbolTableLevel*> table;
+    typedef std::map< TBasicType, TPrecision > PrecisionStackLevel;
+    std::vector< PrecisionStackLevel > precisionStack;
     int uniqueId;     // for unique identification in code generation
 };
 
diff --git a/src/compiler/Types.h b/src/compiler/Types.h
index 0ad9d63..fca0ac1 100644
--- a/src/compiler/Types.h
+++ b/src/compiler/Types.h
@@ -51,7 +51,7 @@
     {
         type = bt;
         qualifier = q;
-        precision = EbpHigh;
+        precision = EbpUndefined;
         size = 1;
         matrix = false;
         array = false;
@@ -60,11 +60,6 @@
         line = ln;
     }
 
-    void setPrecision(TPrecision pcs)
-    {
-        precision = pcs;
-    }
-
     void setAggregate(int s, bool m = false)
     {
         size = s;
@@ -86,7 +81,7 @@
 class TType {
 public:
     POOL_ALLOCATOR_NEW_DELETE(GlobalPoolAllocator)
-    explicit TType(TBasicType t, TQualifier q = EvqTemporary, int s = 1, bool m = false, bool a = false, TPrecision p = EbpHigh) :
+    explicit TType(TBasicType t, TPrecision p, TQualifier q = EvqTemporary, int s = 1, bool m = false, bool a = false) :
                             type(t), precision(p), qualifier(q), size(s), matrix(m), array(a), arraySize(0),
                             structure(0), structureSize(0), maxArraySize(0), arrayInformationType(0), fieldName(0), mangled(0), typeName(0)
                             { }
@@ -99,7 +94,7 @@
                                   typeName = NewPoolTString(p.userDef->getTypeName().c_str());
                               }
                             }
-    explicit TType(TTypeList* userDef, const TString& n, TPrecision p = EbpHigh) :
+    explicit TType(TTypeList* userDef, const TString& n, TPrecision p = EbpUndefined) :
                             type(EbtStruct), precision(p), qualifier(EvqTemporary), size(1), matrix(false), array(false), arraySize(0),
                             structure(userDef), structureSize(0), maxArraySize(0), arrayInformationType(0), fieldName(0), mangled(0) {
                                 typeName = NewPoolTString(n.c_str());
diff --git a/src/compiler/glslang.y b/src/compiler/glslang.y
index 700bd00..9ab9962 100644
--- a/src/compiler/glslang.y
+++ b/src/compiler/glslang.y
@@ -187,7 +187,7 @@
         if (symbol == 0) {
             parseContext->error($1.line, "undeclared identifier", $1.string->c_str(), "");
             parseContext->recover();
-            TType type(EbtFloat);
+            TType type(EbtFloat, EbpUndefined);
             TVariable* fakeVariable = new TVariable($1.string, type);
             parseContext->symbolTable.insert(*fakeVariable);
             variable = fakeVariable;
@@ -229,17 +229,17 @@
         }
         ConstantUnion *unionArray = new ConstantUnion[1];
         unionArray->setIConst($1.i);
-        $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtInt, EvqConst), $1.line);
+        $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConst), $1.line);
     }
     | FLOATCONSTANT {
         ConstantUnion *unionArray = new ConstantUnion[1];
         unionArray->setFConst($1.f);
-        $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtFloat, EvqConst), $1.line);
+        $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpUndefined, EvqConst), $1.line);
     }
     | BOOLCONSTANT {
         ConstantUnion *unionArray = new ConstantUnion[1];
         unionArray->setBConst($1.b);
-        $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtBool, EvqConst), $1.line);
+        $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), $1.line);
     }
     | LEFT_PAREN expression RIGHT_PAREN {
         $$ = $2;
@@ -303,23 +303,23 @@
         if ($$ == 0) {
             ConstantUnion *unionArray = new ConstantUnion[1];
             unionArray->setFConst(0.0f);
-            $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtFloat, EvqConst), $2.line);
+            $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpHigh, EvqConst), $2.line);
         } else if ($1->isArray()) {
             if ($1->getType().getStruct())
                 $$->setType(TType($1->getType().getStruct(), $1->getType().getTypeName()));
             else
-                $$->setType(TType($1->getBasicType(), EvqTemporary, $1->getNominalSize(), $1->isMatrix()));
+                $$->setType(TType($1->getBasicType(), $1->getPrecision(), EvqTemporary, $1->getNominalSize(), $1->isMatrix()));
 
             if ($1->getType().getQualifier() == EvqConst)
                 $$->getTypePointer()->changeQualifier(EvqConst);
         } else if ($1->isMatrix() && $1->getType().getQualifier() == EvqConst)
-            $$->setType(TType($1->getBasicType(), EvqConst, $1->getNominalSize()));
+            $$->setType(TType($1->getBasicType(), $1->getPrecision(), EvqConst, $1->getNominalSize()));
         else if ($1->isMatrix())
-            $$->setType(TType($1->getBasicType(), EvqTemporary, $1->getNominalSize()));
+            $$->setType(TType($1->getBasicType(), $1->getPrecision(), EvqTemporary, $1->getNominalSize()));
         else if ($1->isVector() && $1->getType().getQualifier() == EvqConst)
-            $$->setType(TType($1->getBasicType(), EvqConst));
+            $$->setType(TType($1->getBasicType(), $1->getPrecision(), EvqConst));
         else if ($1->isVector())
-            $$->setType(TType($1->getBasicType(), EvqTemporary));
+            $$->setType(TType($1->getBasicType(), $1->getPrecision(), EvqTemporary));
         else
             $$->setType($1->getType());
     }
@@ -347,19 +347,19 @@
                     $$ = $1;
                 }
                 else
-                    $$->setType(TType($1->getBasicType(), EvqConst, (int) (*$3.string).size()));
+                    $$->setType(TType($1->getBasicType(), $1->getPrecision(), EvqConst, (int) (*$3.string).size()));
             } else {
                 if (fields.num == 1) {
                     ConstantUnion *unionArray = new ConstantUnion[1];
                     unionArray->setIConst(fields.offsets[0]);
-                    TIntermTyped* index = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtInt, EvqConst), $3.line);
+                    TIntermTyped* index = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConst), $3.line);
                     $$ = parseContext->intermediate.addIndex(EOpIndexDirect, $1, index, $2.line);
-                    $$->setType(TType($1->getBasicType()));
+                    $$->setType(TType($1->getBasicType(), $1->getPrecision()));
                 } else {
                     TString vectorString = *$3.string;
                     TIntermTyped* index = parseContext->intermediate.addSwizzle(fields, $3.line);
                     $$ = parseContext->intermediate.addIndex(EOpVectorSwizzle, $1, index, $2.line);
-                    $$->setType(TType($1->getBasicType(),EvqTemporary, (int) vectorString.size()));
+                    $$->setType(TType($1->getBasicType(), $1->getPrecision(), EvqTemporary, (int) vectorString.size()));
                 }
             }
         } else if ($1->isMatrix()) {
@@ -377,15 +377,15 @@
                 parseContext->recover();
                 ConstantUnion *unionArray = new ConstantUnion[1];
                 unionArray->setIConst(0);
-                TIntermTyped* index = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtInt, EvqConst), $3.line);
+                TIntermTyped* index = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConst), $3.line);
                 $$ = parseContext->intermediate.addIndex(EOpIndexDirect, $1, index, $2.line);
-                $$->setType(TType($1->getBasicType(), EvqTemporary, $1->getNominalSize()));
+                $$->setType(TType($1->getBasicType(), $1->getPrecision(),EvqTemporary, $1->getNominalSize()));
             } else {
                 ConstantUnion *unionArray = new ConstantUnion[1];
                 unionArray->setIConst(fields.col * $1->getNominalSize() + fields.row);
-                TIntermTyped* index = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtInt, EvqConst), $3.line);
+                TIntermTyped* index = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConst), $3.line);
                 $$ = parseContext->intermediate.addIndex(EOpIndexDirect, $1, index, $2.line);
-                $$->setType(TType($1->getBasicType()));
+                $$->setType(TType($1->getBasicType(), $1->getPrecision()));
             }
         } else if ($1->getBasicType() == EbtStruct) {
             bool fieldFound = false;
@@ -477,7 +477,7 @@
             // Don't go through the symbol table for constructors.
             // Their parameters will be verified algorithmically.
             //
-            TType type(EbtVoid);  // use this to get the type back
+            TType type(EbtVoid, EbpUndefined);  // use this to get the type back
             if (parseContext->constructorErrorCheck($1.line, $1.intermNode, *fnCall, op, &type)) {
                 $$ = 0;
             } else {
@@ -555,7 +555,7 @@
                 // Put on a dummy node for error recovery
                 ConstantUnion *unionArray = new ConstantUnion[1];
                 unionArray->setFConst(0.0f);
-                $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtFloat, EvqConst), $1.line);
+                $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpUndefined, EvqConst), $1.line);
                 parseContext->recover();
             }
         }
@@ -687,14 +687,14 @@
     | IDENTIFIER {
         if (parseContext->reservedErrorCheck($1.line, *$1.string))
             parseContext->recover();
-        TType type(EbtVoid);
+        TType type(EbtVoid, EbpUndefined);
         TFunction *function = new TFunction($1.string, type);
         $$ = function;
     }
     | FIELD_SELECTION {
         if (parseContext->reservedErrorCheck($1.line, *$1.string))
             parseContext->recover();
-        TType type(EbtVoid);
+        TType type(EbtVoid, EbpUndefined);
         TFunction *function = new TFunction($1.string, type);
         $$ = function;
     }
@@ -806,7 +806,7 @@
             parseContext->recover();
             ConstantUnion *unionArray = new ConstantUnion[1];
             unionArray->setBConst(false);
-            $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtBool, EvqConst), $2.line);
+            $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), $2.line);
         }
     }
     | relational_expression RIGHT_ANGLE shift_expression  {
@@ -816,7 +816,7 @@
             parseContext->recover();
             ConstantUnion *unionArray = new ConstantUnion[1];
             unionArray->setBConst(false);
-            $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtBool, EvqConst), $2.line);
+            $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), $2.line);
         }
     }
     | relational_expression LE_OP shift_expression  {
@@ -826,7 +826,7 @@
             parseContext->recover();
             ConstantUnion *unionArray = new ConstantUnion[1];
             unionArray->setBConst(false);
-            $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtBool, EvqConst), $2.line);
+            $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), $2.line);
         }
     }
     | relational_expression GE_OP shift_expression  {
@@ -836,7 +836,7 @@
             parseContext->recover();
             ConstantUnion *unionArray = new ConstantUnion[1];
             unionArray->setBConst(false);
-            $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtBool, EvqConst), $2.line);
+            $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), $2.line);
         }
     }
     ;
@@ -850,7 +850,7 @@
             parseContext->recover();
             ConstantUnion *unionArray = new ConstantUnion[1];
             unionArray->setBConst(false);
-            $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtBool, EvqConst), $2.line);
+            $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), $2.line);
         } else if (($1->isArray() || $3->isArray()) && parseContext->extensionErrorCheck($2.line, "GL_3DL_array_objects"))
             parseContext->recover();
     }
@@ -861,7 +861,7 @@
             parseContext->recover();
             ConstantUnion *unionArray = new ConstantUnion[1];
             unionArray->setBConst(false);
-            $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtBool, EvqConst), $2.line);
+            $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), $2.line);
         } else if (($1->isArray() || $3->isArray()) && parseContext->extensionErrorCheck($2.line, "GL_3DL_array_objects"))
             parseContext->recover();
     }
@@ -888,7 +888,7 @@
             parseContext->recover();
             ConstantUnion *unionArray = new ConstantUnion[1];
             unionArray->setBConst(false);
-            $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtBool, EvqConst), $2.line);
+            $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), $2.line);
         }
     }
     ;
@@ -902,7 +902,7 @@
             parseContext->recover();
             ConstantUnion *unionArray = new ConstantUnion[1];
             unionArray->setBConst(false);
-            $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtBool, EvqConst), $2.line);
+            $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), $2.line);
         }
     }
     ;
@@ -916,7 +916,7 @@
             parseContext->recover();
             ConstantUnion *unionArray = new ConstantUnion[1];
             unionArray->setBConst(false);
-            $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtBool, EvqConst), $2.line);
+            $$ = parseContext->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), $2.line);
         }
     }
     ;
@@ -1016,6 +1016,7 @@
         $$ = $1.intermAggregate;
     }
     | PRECISION precision_qualifier type_specifier_no_prec SEMICOLON {
+        parseContext->symbolTable.setDefaultPrecision( $3.type, $2 );
         $$ = 0;
     }
     ;
@@ -1209,6 +1210,13 @@
 init_declarator_list
     : single_declaration {
         $$ = $1;
+        
+        if ($$.type.precision == EbpUndefined) {
+            $$.type.precision = parseContext->symbolTable.getDefaultPrecision($1.type.type);
+            if (parseContext->precisionErrorCheck($1.line, $$.type.precision, $1.type.type)) {
+                parseContext->recover();
+            }
+        }
     }
     | init_declarator_list COMMA IDENTIFIER {
         $$.intermAggregate = parseContext->intermediate.growAggregate($1.intermNode, parseContext->intermediate.addSymbol(0, *$3.string, TType($1.type), $3.line), $3.line);
@@ -1593,7 +1601,7 @@
     }
     | precision_qualifier type_specifier_no_prec {
         $$ = $2;
-    $$.setPrecision($1);
+        $$.precision = $1;
     }
     ;
 
@@ -1813,12 +1821,12 @@
 
 struct_declarator
     : IDENTIFIER {
-        $$.type = new TType(EbtVoid);
+        $$.type = new TType(EbtVoid, EbpUndefined);
         $$.line = $1.line;
         $$.type->setFieldName(*$1.string);
     }
     | IDENTIFIER LEFT_BRACKET constant_expression RIGHT_BRACKET {
-        $$.type = new TType(EbtVoid);
+        $$.type = new TType(EbtVoid, EbpUndefined);
         $$.line = $1.line;
         $$.type->setFieldName(*$1.string);
 
diff --git a/src/compiler/intermOut.cpp b/src/compiler/intermOut.cpp
index a5bd069..70ead52 100644
--- a/src/compiler/intermOut.cpp
+++ b/src/compiler/intermOut.cpp
@@ -41,7 +41,7 @@
     char *p = &buf[0];
 
     if (qualifier != EvqTemporary && qualifier != EvqGlobal)
-        p += sprintf(p, "%s ", getQualifierString());
+        p += sprintf(p, "%s %s ", getQualifierString(), getPrecisionString());
     if (array)
         p += sprintf(p, "array of ");
     if (matrix)
diff --git a/src/compiler/intermediate.h b/src/compiler/intermediate.h
index ea7a962..b1241ca 100644
--- a/src/compiler/intermediate.h
+++ b/src/compiler/intermediate.h
@@ -238,6 +238,7 @@
 
     virtual TBasicType getBasicType() const { return type.getBasicType(); }
     virtual TQualifier getQualifier() const { return type.getQualifier(); }
+    virtual TPrecision getPrecision() const { return type.getPrecision(); }
     virtual int getNominalSize() const { return type.getNominalSize(); }
     virtual int getSize() const { return type.getInstanceSize(); }
     virtual bool isMatrix() const { return type.isMatrix(); }
@@ -334,7 +335,7 @@
     bool isConstructor() const;
     virtual bool promote(TInfoSink&) { return true; }
 protected:
-    TIntermOperator(TOperator o) : TIntermTyped(TType(EbtFloat)), op(o) {}
+    TIntermOperator(TOperator o) : TIntermTyped(TType(EbtFloat, EbpUndefined)), op(o) {}
     TIntermOperator(TOperator o, TType& t) : TIntermTyped(t), op(o) {}   
     TOperator op;
 };
@@ -416,7 +417,7 @@
 class TIntermSelection : public TIntermTyped {
 public:
     TIntermSelection(TIntermTyped* cond, TIntermNode* trueB, TIntermNode* falseB) :
-            TIntermTyped(TType(EbtVoid)), condition(cond), trueBlock(trueB), falseBlock(falseB) {}
+            TIntermTyped(TType(EbtVoid, EbpUndefined)), condition(cond), trueBlock(trueB), falseBlock(falseB) {}
     TIntermSelection(TIntermTyped* cond, TIntermNode* trueB, TIntermNode* falseB, const TType& type) :
             TIntermTyped(type), condition(cond), trueBlock(trueB), falseBlock(falseB) {}
     virtual void traverse(TIntermTraverser*);