diff --git a/Test/120.vert b/Test/120.vert
index 9e8afbc3593695cd8cadd9f39941ea93787b24ff..d0f64e402f48254f3cff2dcbf7c3aa0a4e198f1f 100644
--- a/Test/120.vert
+++ b/Test/120.vert
@@ -8,8 +8,9 @@ attribute vec4 attv4;
 uniform sampler2D s2D;
 invariant varying vec2 centTexCoord;
 invariant gl_Position;
-
+centroid gl_Position;
 centroid centroid foo;
+invariant gl_Position, gl_PointSize;
 
 void main()
 {
@@ -22,6 +23,8 @@ void main()
 
     float f[];
     int a = f.length();
+
+    gl_PointSize = 3.8;
 }
 
 uniform float initted = 3.4;
diff --git a/Test/specExamples.vert b/Test/specExamples.vert
index c4b283c2cef5fef194ba0c5f23082887a0cf22c9..bf148fa073f665e4794f43ca4c73e07f6adecc11 100644
--- a/Test/specExamples.vert
+++ b/Test/specExamples.vert
@@ -97,8 +97,8 @@ layout (binding=3) uniform atomic_uint c2;           // offset = 8
 layout (binding=2) uniform atomic_uint d2;           // offset = 4
 
 //layout (offset=4)                // error, must include binding
-layout (binding=1, offset=0)  a; // okay
-layout (binding=2, offset=0)  b; // okay
+//layout (binding=1, offset=0)  a; // okay
+//layout (binding=2, offset=0)  b; // okay
 //layout (binding=1, offset=0)  c; // error, offsets must not be shared
 //                                 //        between a and c
 //layout (binding=1, offset=2)  d; // error, overlaps offset 0 of a
diff --git a/glslang/Include/Types.h b/glslang/Include/Types.h
index 02f991b0b25c8e52a0e3de57365a78ce8d051423..5fd3a7d0147c692882f9291230fc88ef7e938779 100644
--- a/glslang/Include/Types.h
+++ b/glslang/Include/Types.h
@@ -154,6 +154,14 @@ inline TTypeList* NewPoolTTypeList()
 	return new(memory) TTypeList;
 }
 
+typedef TVector<TString*> TIdentifierList;
+
+inline TIdentifierList* NewPoolTIdentifierList()
+{
+    void* memory = GlobalPoolAllocator.allocate(sizeof(TIdentifierList));
+    return new(memory) TIdentifierList;
+}
+
 //
 // TODO: memory: TArraySizes can be replaced by something smaller.
 // Almost all arrays could be handled by two sizes each fitting
@@ -236,7 +244,7 @@ public:
 
     bool isMemory() const
     {
-        return coherent || volatil || restrict || readonly || writeonly;
+        return shared || coherent || volatil || restrict || readonly || writeonly;
     }
     bool isInterpolation() const
     {
diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp
index 9e396340319438b76c4ab9d773e92c86c460cbf0..a32ea21a3a0ba98bb3e83dc74ea568fb7bf14395 100644
--- a/glslang/MachineIndependent/ParseHelper.cpp
+++ b/glslang/MachineIndependent/ParseHelper.cpp
@@ -48,12 +48,15 @@ TParseContext::TParseContext(TSymbolTable& symt, TIntermediate& interm, int v, E
             version(v), profile(p), forwardCompatible(fc), messages(m),
             contextPragma(true, false)
 {
+    // set all precision defaults to EpqNone, which is correct for all desktop types
+    // and for ES types that don't have defaults (thus getting an error on use)
     for (int type = 0; type < EbtNumTypes; ++type)
         defaultPrecision[type] = EpqNone;
 
     for (int type = 0; type < maxSamplerIndex; ++type)
         defaultSamplerPrecision[type] = EpqNone;
 
+    // replace with real defaults for those that have them
     if (profile == EEsProfile) {
         TSampler sampler;
         sampler.set(EbtFloat, Esd2D);
@@ -67,13 +70,11 @@ TParseContext::TParseContext(TSymbolTable& symt, TIntermediate& interm, int v, E
             defaultPrecision[EbtUint] = EpqHigh;
             defaultPrecision[EbtFloat] = EpqHigh;
             defaultPrecision[EbtSampler] = EpqLow;
-            // TODO: functionality: need default precisions per sampler type
             break;
         case EShLangFragment:
             defaultPrecision[EbtInt] = EpqMedium;
             defaultPrecision[EbtUint] = EpqMedium;
             defaultPrecision[EbtSampler] = EpqLow;
-            // TODO: semantics: give error when using float in frag shader without default precision
             break;
         default:
             error(1, "INTERNAL ERROR", "unexpected language", "");
@@ -1607,6 +1608,36 @@ void TParseContext::addBlock(int line, TTypeList& typeList, const TString* insta
     }
 }
 
+// For an identifier that is already declared, add more qualification to it.
+void TParseContext::addQualifierToExisting(int line, TQualifier qualifier, const TString& identifier)
+{
+    TSymbol* existing = symbolTable.find(identifier);
+    TVariable* variable = existing ? existing->getAsVariable() : 0;
+    if (! variable) {
+        error(line, "identifier not previously declared", identifier.c_str(), "");
+
+        return;
+    }
+
+    if (qualifier.isAuxillary() || 
+        qualifier.isMemory() ||
+        qualifier.isInterpolation() ||
+        qualifier.storage != EvqTemporary ||
+        qualifier.precision != EpqNone) {
+        error(line, "cannot add this qualifier to an existing variable", identifier.c_str(), "");
+
+        return;
+    }
+
+    variable->getType().getQualifier().invariant = true;
+}
+
+void TParseContext::addQualifierToExisting(int line, TQualifier qualifier, TIdentifierList& identifiers)
+{
+    for (unsigned int i = 0; i < identifiers.size(); ++i)
+        addQualifierToExisting(line, qualifier, *identifiers[i]);
+}
+
 //
 // Take the sequence of statements that has been built up since the last case/default,
 // put it on the list of top-level nodes for the current (inner-most) switch statement,
diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h
index d5a215a2ae8c2cefc589eb54748d2a1a4e237851..04a80b59d95ea2f8e8b76ba701c5751871e01916 100644
--- a/glslang/MachineIndependent/ParseHelper.h
+++ b/glslang/MachineIndependent/ParseHelper.h
@@ -154,6 +154,8 @@ struct TParseContext {
     TIntermTyped* constructStruct(TIntermNode*, const TType&, int, TSourceLoc);
     TIntermTyped* constructBuiltIn(const TType&, TOperator, TIntermNode*, TSourceLoc, bool subset);
     void addBlock(int line, TTypeList& typeList, const TString* instanceName = 0, TArraySizes arraySizes = 0);
+    void addQualifierToExisting(int line, TQualifier, const TString& identifier);
+    void addQualifierToExisting(int line, TQualifier, TIdentifierList&);
     void wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode);
     TIntermNode* addSwitch(int line, TIntermTyped* expression, TIntermAggregate* body);
     void updateDefaults(int line, const TPublicType&, const TString* id);
diff --git a/glslang/MachineIndependent/glslang.y b/glslang/MachineIndependent/glslang.y
index a7995f94aa22806504c16c4ab191ef4f81cddae7..42e0635b6992bff92e8c7707f7bae58a1402c31c 100644
--- a/glslang/MachineIndependent/glslang.y
+++ b/glslang/MachineIndependent/glslang.y
@@ -103,6 +103,7 @@ extern void yyerror(const char*);
             TTypeLine typeLine;
             TTypeList* typeList;
             TArraySizes arraySizes;
+            TIdentifierList* identifierList;
         };
     } interm;
 }
@@ -214,6 +215,8 @@ extern void yyerror(const char*);
 %type <interm> function_call_header_with_parameters function_call_header_no_parameters function_call_generic function_prototype
 %type <interm> function_call_or_method function_identifier function_call_header
 
+%type <interm.identifierList> identifier_list
+
 %start translation_unit
 %%
 
@@ -1120,15 +1123,12 @@ declaration
         $$ = 0;
     }
     | type_qualifier IDENTIFIER SEMICOLON {
-        // TODO: functionality: track what variables are declared with INVARIANT
-        // precise foo;
-        // invariant foo;
+        parseContext.addQualifierToExisting($1.line, $1.qualifier, *$2.string);
         $$ = 0;
     }
     | type_qualifier IDENTIFIER identifier_list SEMICOLON {
-        // TODO: functionality: track what variables are declared with INVARIANT
-        // precise foo, bar;
-        // invariant foo, bar;
+        $3->push_back($2.string);
+        parseContext.addQualifierToExisting($1.line, $1.qualifier, *$3);
         $$ = 0;
     }
     ;
@@ -1144,8 +1144,12 @@ block_structure
 
 identifier_list
     : COMMA IDENTIFIER {
+        $$ = NewPoolTIdentifierList();
+        $$->push_back($2.string);
     }
     | identifier_list COMMA IDENTIFIER {
+        $$ = $1;
+        $$->push_back($3.string);
     }
     ;