diff --git a/Install/Windows/glslangValidator.exe b/Install/Windows/glslangValidator.exe
index 600014cd9c6291b3989a5a15f7e3316a4e576085..6e5c328c8fd66e952f8b8965205d24baf1332abd 100644
Binary files a/Install/Windows/glslangValidator.exe and b/Install/Windows/glslangValidator.exe differ
diff --git a/Test/100.frag b/Test/100.frag
index 529b3600c02b36ae5b5949f037500290a494f515..61107c2b0084198d4d6496698a131982f52d8df2 100644
--- a/Test/100.frag
+++ b/Test/100.frag
@@ -43,3 +43,36 @@ void main()
 
 invariant gl_FragColor;
 float fa[];              // ERROR
+float f13;
+invariant f13;           // ERROR
+struct S { int a; };
+invariant S;
+invariant float fi;      // ERROR
+varying vec4 av;
+invariant av;            // okay in v100
+
+void foo10()
+{
+    invariant f;         // ERROR
+    invariant float f2;  // ERROR
+    float f3;
+    invariant f3;        // ERROR
+}
+
+uniform vec2 uv2;
+invariant uv2;              // ERROR
+invariant uniform vec3 uv3; // ERROR
+
+sampler2D glob2D;           // ERROR
+void f11(sampler2D p2d)
+{
+    sampler2D v2D;          // ERROR
+}
+varying sampler2D vary2D;   // ERROR
+
+struct sp {
+    highp float f;
+    in float g;             // ERROR
+    uniform float h;        // ERROR
+    invariant float i;      // ERROR
+};
diff --git a/Test/300.frag b/Test/300.frag
index d63c81e26bd326ec85cd11193566a8cf5520319c..30343992dd5cd214f84cd3098b39ce97094641c8 100644
--- a/Test/300.frag
+++ b/Test/300.frag
@@ -36,7 +36,7 @@ struct s {
     sampler2D s;
 };
 
-out s badout;               // ERROR
+in s badout;               // ERROR, can't contain a sampler
 
 struct S2 {
     vec3 c;
@@ -108,7 +108,19 @@ out vec4 colors[4];
 void foo()
 {
     colors[2] = c4D;
-    colors[ic1D] = c4D;
+    colors[ic1D] = c4D;  // ERROR
+}
+
+uniform s st1;
+uniform s st2;
+
+void foo13(s inSt2)
+{
+    if (st1 == st2);  // ERROR
+    if (st1 != st2);  // ERROR
+    st1.s == st2.s;   // ERROR
+    inSt2 = st1;      // ERROR
+    inSt2 == st1;     // ERROR
 }
 
 float imageBuffer;    // ERROR, reserved
diff --git a/Test/300.vert b/Test/300.vert
index 7f5f32681fdc524270a6a2a23e1fcdb715088553..01ececec2897727c84f51b60d15d270306b6babf 100644
--- a/Test/300.vert
+++ b/Test/300.vert
@@ -66,3 +66,17 @@ uniform ub {
 } ubInst[];         // ERROR
 void foo(int a[]);  // ERROR
 float okayA[] = float[](3.0, 4.0);  // Okay
+
+out vec3 newV;
+void newVFun()
+{
+    newV = v3;
+}
+
+invariant newV;  // ERROR, variable already used
+in vec4 invIn;
+invariant invIn; // ERROR, in v300
+out S s2;
+invariant s2;
+invariant out S s3;
+flat out int;
diff --git a/Test/430.vert b/Test/430.vert
index 54cf775af156d71381ffe0ad6628d30ba6764dfd..6912fb4e39f546b697de906f8932c275dde740fc 100644
--- a/Test/430.vert
+++ b/Test/430.vert
@@ -15,3 +15,19 @@ void foo()
 {
     gl_ClipDistance[2] = 3.7;
 }
+
+struct sp {
+    highp float f;
+    in float g;             // ERROR
+    uniform float h;        // ERROR
+    invariant float i;      // ERROR
+    volatile float j;       // ERROR
+    layout(row_major) mat3 m3; // ERROR
+};
+
+void foo3(invariant vec4 v4,                 // ERROR
+          volatile vec3 v3,
+          layout(location = 3) vec2 v2,      // ERROR
+          centroid vec3 cv3)                 // ERROR
+{
+}
\ No newline at end of file
diff --git a/Test/baseResults/100.frag.out b/Test/baseResults/100.frag.out
index 5c376eba86f037235c525e2252b2713eecf59b67..8c7b64fa090d6e29f3eb720f1185c0533c185da9 100644
--- a/Test/baseResults/100.frag.out
+++ b/Test/baseResults/100.frag.out
@@ -20,7 +20,27 @@ ERROR: 0:38: 'array comparison' : not supported for this version or the enabled
 ERROR: 0:40: 'switch' : Reserved word. 
 ERROR: 0:40: 'switch statements' : not supported for this version or the enabled extensions 
 ERROR: 0:45: '' : array size required 
-ERROR: 22 compilation errors.  No code generated.
+ERROR: 0:47: 'invariant' : can only apply to an output or an input in a non-vertex stage
+ 
+ERROR: 0:50: 'invariant' : can only apply to an output or an input in a non-vertex stage
+ 
+ERROR: 0:56: 'invariant' : can only apply to an output or an input in a non-vertex stage
+ 
+ERROR: 0:57: 'invariant' : can only apply to an output or an input in a non-vertex stage
+ 
+ERROR: 0:59: 'invariant' : can only apply to an output or an input in a non-vertex stage
+ 
+ERROR: 0:63: 'invariant' : can only apply to an output or an input in a non-vertex stage
+ 
+ERROR: 0:64: 'invariant' : can only apply to an output or an input in a non-vertex stage
+ 
+ERROR: 0:66: 'sampler2D' : sampler/image types can only be used in uniform variables or function parameters: glob2D
+ERROR: 0:69: 'sampler2D' : sampler/image types can only be used in uniform variables or function parameters: v2D
+ERROR: 0:71: 'sampler2D' : sampler/image types can only be used in uniform variables or function parameters: vary2D
+ERROR: 0:75: 'g' : cannot use storage or interpolation qualifiers on structure members 
+ERROR: 0:76: 'h' : cannot use storage or interpolation qualifiers on structure members 
+ERROR: 0:77: 'i' : cannot use invariant qualifier on structure members 
+ERROR: 35 compilation errors.  No code generated.
 
 ERROR: node is still EOpNull!
 0:3  Sequence
@@ -96,6 +116,11 @@ ERROR: node is still EOpNull!
 0:38          's2' (structure{f,a})
 0:38        true case is null
 0:40      'b' (mediump int)
+0:54  Function Definition: foo10( (void)
+0:54    Function Parameters: 
+0:67  Function Definition: f11(s21; (void)
+0:67    Function Parameters: 
+0:67      'p2d' (in lowp sampler2D)
 0:?   Linker Objects
 0:?     'a' (3-element array of mediump int)
 0:?     'uint' (mediump int)
@@ -103,4 +128,11 @@ ERROR: node is still EOpNull!
 0:?     'f' (mediump float)
 0:?     '__anon__0' (layout(column_major shared ) uniform block{x})
 0:?     'fa' (unsized array of mediump float)
+0:?     'f13' (mediump float)
+0:?     'fi' (invariant mediump float)
+0:?     'av' (smooth in mediump 4-component vector of float)
+0:?     'uv2' (uniform mediump 2-component vector of float)
+0:?     'uv3' (invariant uniform mediump 3-component vector of float)
+0:?     'glob2D' (lowp sampler2D)
+0:?     'vary2D' (smooth in lowp sampler2D)
 
diff --git a/Test/baseResults/300.frag.out b/Test/baseResults/300.frag.out
index 2e0d6575e19b19eb62fae6652ad40f19b962128a..622033d0c6d886c6b641183eed686cc922e57f2f 100644
--- a/Test/baseResults/300.frag.out
+++ b/Test/baseResults/300.frag.out
@@ -1,8 +1,8 @@
 ERROR: 0:30: 'noperspective' : Reserved word. 
 ERROR: 0:30: 'noperspective' : not supported with this profile: es
-ERROR: 0:31: 'sampler/image' : samplers and images must be uniform 
+ERROR: 0:31: 'sampler2D' : sampler/image types can only be used in uniform variables or function parameters: bads
 ERROR: 0:32: 'uint' : cannot apply precision statement to this type; use 'float', 'int' or a sampler type 
-ERROR: 0:39: 'structure' : samplers and images must be uniform (structure cannot contain a sampler or image)
+ERROR: 0:39: 'structure' : non-uniform struct contains a sampler or image: badout
 ERROR: 0:69: 'variable indexing sampler array' : not supported with this profile: es
 ERROR: 0:83: 'double' : Reserved word. 
 ERROR: 0:83: 'double' : not supported with this profile: es
@@ -18,9 +18,15 @@ ERROR: 0:102: 'arrays of arrays' : not supported with this profile: es
 ERROR: 0:103: 'arrays of arrays' : not supported with this profile: es
 ERROR: 0:100: 'arrays of arrays' : not supported with this profile: es
 ERROR: 0:111: 'variable indexing fragment shader ouput array' : not supported with this profile: es
-ERROR: 0:114: 'imageBuffer' : Reserved word. 
-ERROR: 0:114: '' :  syntax error
-ERROR: 22 compilation errors.  No code generated.
+ERROR: 0:119: '==' : can't use with samplers or structs containing samplers 
+ERROR: 0:120: '!=' : can't use with samplers or structs containing samplers 
+ERROR: 0:121: '==' : can't use with samplers or structs containing samplers 
+ERROR: 0:121: '==' :  wrong operand types: no operation '==' exists that takes a left-hand operand of type 'lowp sampler2D' and a right operand of type 'lowp sampler2D' (or there is no acceptable conversion)
+ERROR: 0:122: '=' : can't use with samplers or structs containing samplers 
+ERROR: 0:123: '==' : can't use with samplers or structs containing samplers 
+ERROR: 0:126: 'imageBuffer' : Reserved word. 
+ERROR: 0:126: '' :  syntax error
+ERROR: 28 compilation errors.  No code generated.
 
 ERROR: node is still EOpNull!
 0:53  Function Definition: main( (void)
@@ -223,6 +229,30 @@ ERROR: node is still EOpNull!
 0:111          'colors' (out 4-element array of lowp 4-component vector of float)
 0:111          'ic1D' (flat in mediump int)
 0:111        'c4D' (smooth lowp 4-component vector of float)
+0:117  Function Definition: foo13(struct-s-i1-s211; (void)
+0:117    Function Parameters: 
+0:117      'inSt2' (in structure{i,s})
+0:119    Sequence
+0:119      Test condition and select (void)
+0:119        Condition
+0:119        Compare Equal (bool)
+0:119          'st1' (uniform structure{i,s})
+0:119          'st2' (uniform structure{i,s})
+0:119        true case is null
+0:120      Test condition and select (void)
+0:120        Condition
+0:120        Compare Not Equal (bool)
+0:120          'st1' (uniform structure{i,s})
+0:120          'st2' (uniform structure{i,s})
+0:120        true case is null
+0:121      Constant:
+0:121        false (const bool)
+0:122      move second child to first child (structure{i,s})
+0:122        'inSt2' (in structure{i,s})
+0:122        'st1' (uniform structure{i,s})
+0:123      Compare Equal (bool)
+0:123        'inSt2' (in structure{i,s})
+0:123        'st1' (uniform structure{i,s})
 0:?   Linker Objects
 0:?     's2D' (uniform lowp sampler2D)
 0:?     's3D' (uniform lowp sampler3D)
@@ -249,11 +279,13 @@ ERROR: node is still EOpNull!
 0:?     'ic4D' (flat in mediump 4-component vector of int)
 0:?     'badv' (noperspective in lowp 4-component vector of float)
 0:?     'bads' (smooth in lowp sampler2D)
-0:?     'badout' (out structure{i,s})
+0:?     'badout' (smooth in structure{i,s})
 0:?     's2' (smooth in structure{c,f})
 0:?     'sc' (out lowp 3-component vector of float)
 0:?     'sf' (out lowp float)
 0:?     'arrayedSampler' (uniform 5-element array of lowp sampler2D)
 0:?     'multiInst' (layout(column_major shared ) uniform 2-element array of block{a,b,c})
 0:?     'colors' (out 4-element array of lowp 4-component vector of float)
+0:?     'st1' (uniform structure{i,s})
+0:?     'st2' (uniform structure{i,s})
 
diff --git a/Test/baseResults/300.vert.out b/Test/baseResults/300.vert.out
index 38826088ce03082c703339e75366342f7b8bfabd..8000c70f1a34c6471ac87283fea268520be44555 100644
--- a/Test/baseResults/300.vert.out
+++ b/Test/baseResults/300.vert.out
@@ -18,7 +18,9 @@ ERROR: 0:63: '' : array size required
 ERROR: 0:65: '' : array size required 
 ERROR: 0:64: '' : array size required 
 ERROR: 0:67: '' : array size required 
-ERROR: 20 compilation errors.  No code generated.
+ERROR: 0:78: 'invariant' : can only apply to an output
+ invIn
+ERROR: 21 compilation errors.  No code generated.
 
 ERROR: node is still EOpNull!
 0:27  Function Definition: main( (void)
@@ -136,6 +138,12 @@ ERROR: node is still EOpNull!
 0:68      Constant:
 0:68        3.000000
 0:68        4.000000
+0:71  Function Definition: newVFun( (void)
+0:71    Function Parameters: 
+0:73    Sequence
+0:73      move second child to first child (highp 3-component vector of float)
+0:73        'newV' (smooth out highp 3-component vector of float)
+0:73        'v3' (in highp 3-component vector of float)
 0:?   Linker Objects
 0:?     'm43' (uniform highp 4X3 matrix of float)
 0:?     'm33' (uniform highp 3X3 matrix of float)
@@ -155,6 +163,10 @@ ERROR: node is still EOpNull!
 0:?     'badsize2' (unsized array of highp float)
 0:?     'ubInst' (layout(column_major shared ) uniform unsized array of block{a})
 0:?     'okayA' (2-element array of highp float)
+0:?     'newV' (smooth out highp 3-component vector of float)
+0:?     'invIn' (in highp 4-component vector of float)
+0:?     's2' (smooth out structure{c,f})
+0:?     's3' (invariant smooth out structure{c,f})
 0:?     'gl_VertexID' (gl_VertexId highp int)
 0:?     'gl_InstanceID' (gl_InstanceId highp int)
 
diff --git a/Test/baseResults/430.vert.out b/Test/baseResults/430.vert.out
index 166d04756ca4ee6171400b06e2b0cf876c09a99d..8500615f2f897460dc287b7c1c36cdb1dda7c79a 100644
--- a/Test/baseResults/430.vert.out
+++ b/Test/baseResults/430.vert.out
@@ -2,7 +2,15 @@ Warning, version 430 is not yet complete; some version-specific features are pre
 ERROR: 0:3: 'v4' : location qualifiers only appy to uniform, buffer, in, or out storage qualifiers 
 ERROR: 0:7: 'location qualifier on input block' : not supported for this version or the enabled extensions 
 ERROR: 0:8: 'location qualifier on output block' : not supported for this version or the enabled extensions 
-ERROR: 3 compilation errors.  No code generated.
+ERROR: 0:21: 'g' : cannot use storage or interpolation qualifiers on structure members 
+ERROR: 0:22: 'h' : cannot use storage or interpolation qualifiers on structure members 
+ERROR: 0:23: 'i' : cannot use invariant qualifier on structure members 
+ERROR: 0:24: 'j' : cannot use memory qualifiers on structure members 
+ERROR: 0:25: 'm3' : cannot use layout qualifiers on structure members 
+ERROR: 0:28: '' : cannot use invariant qualifier on a function parameter 
+ERROR: 0:30: '' : cannot use layout qualifiers on a function parameter 
+ERROR: 0:31: '' : cannot use auxiliary or interpolation qualifiers on a function parameter 
+ERROR: 11 compilation errors.  No code generated.
 
 ERROR: node is still EOpNull!
 0:14  Function Definition: foo( (void)
@@ -18,6 +26,12 @@ ERROR: node is still EOpNull!
 0:16            2 (const int)
 0:16        Constant:
 0:16          3.700000
+0:31  Function Definition: foo3(vf4;vf3;vf2;vf3; (void)
+0:31    Function Parameters: 
+0:31      'v4' (in 4-component vector of float)
+0:31      'v3' (in 3-component vector of float)
+0:31      'v2' (in 2-component vector of float)
+0:31      'cv3' (in 3-component vector of float)
 0:?   Linker Objects
 0:?     'v4' (layout(location=3 ) 4-component vector of float)
 0:?     'uv4' (layout(location=3 ) uniform 4-component vector of float)
diff --git a/Test/baseResults/specExamples.frag.out b/Test/baseResults/specExamples.frag.out
index 19a1c155abe53f71b17e87bc807595e71e9689d8..f5afa45015df6da3e66ad42b48f1e1ff309c3d7d 100644
--- a/Test/baseResults/specExamples.frag.out
+++ b/Test/baseResults/specExamples.frag.out
@@ -3,6 +3,8 @@ ERROR: 0:6: '=' :  cannot convert from 'const uint' to 'int'
 ERROR: 0:20: '' : numeric literal too big 
 ERROR: 0:21: '' : hexidecimal literal too big 
 ERROR: 0:37: 'view' : redefinition 
+ERROR: 0:63: 'invariant' : can only apply to an output
+ Color
 ERROR: 0:68: 'lightPosition' : redefinition 
 ERROR: 0:75: 'Atten' : member storage qualifier cannot contradict block storage qualifier 
 ERROR: 0:87: 'Color' : redefinition 
@@ -46,7 +48,7 @@ ERROR: 0:226: 'in' : not allowed in nested scope
 ERROR: 0:227: 'in' : not allowed in nested scope 
 ERROR: 0:228: 'in' : not allowed in nested scope 
 ERROR: 0:232: 'out' : not allowed in nested scope 
-ERROR: 47 compilation errors.  No code generated.
+ERROR: 48 compilation errors.  No code generated.
 
 gl_FragCoord pixel center is integer
 gl_FragCoord origin is upper left
diff --git a/glslang/Include/Types.h b/glslang/Include/Types.h
index 8edf8bd9d9f9fa2c6ad4fcdab008a0690ee45a68..330df775951e9412f7204e1f9563b749c14f13cb 100644
--- a/glslang/Include/Types.h
+++ b/glslang/Include/Types.h
@@ -768,7 +768,7 @@ public:
 
         *p = 0;
         TString s(buf);
-        s.append(getCompleteTypeString());
+        s.append(getBasicTypeString());
 
         // Add struct/block members
         if (structure) {
@@ -784,7 +784,7 @@ public:
         return s;
     }
 
-    TString getCompleteTypeString() const
+    TString getBasicTypeString() const
     {
         if (basicType == EbtSampler)
             return sampler.getString();
diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp
index d21d95dde0bf2329490c636de258668d5aa0cbdc..565abba25c3de9ad0702de390467e6a5060e8376 100644
--- a/glslang/MachineIndependent/ParseHelper.cpp
+++ b/glslang/MachineIndependent/ParseHelper.cpp
@@ -415,7 +415,7 @@ TIntermTyped* TParseContext::handleVariable(TSourceLoc loc, TSymbol* symbol, TSt
             TType* type;
             if (variable->isReadOnly()) {
                 type = new TType;
-                // break sharing with built-ins
+                // break type sharing with built-ins
                 type->deepCopy(variable->getType());
 
                 // track use of unredeclared gl_FragCoord
@@ -477,7 +477,7 @@ TIntermTyped* TParseContext::handleBracketDereference(TSourceLoc loc, TIntermTyp
                 error(loc, "", "[", "array must be redeclared with a size before being indexed with a variable");
             if (base->getBasicType() == EbtBlock)
                 requireProfile(base->getLoc(), ~EEsProfile, "variable indexing block array");
-            else if (language == EShLangFragment && base->getQualifier().storage == EvqVaryingOut)
+            else if (language == EShLangFragment && base->getQualifier().isPipeOutput())
                 requireProfile(base->getLoc(), ~EEsProfile, "variable indexing fragment shader ouput array");
             else if (base->getBasicType() == EbtSampler && version >= 130) {
                 const char* explanation = "variable indexing sampler array";
@@ -720,7 +720,7 @@ TFunction* TParseContext::handleFunctionDeclarator(TSourceLoc loc, TFunction& fu
     const TFunction* prevDec = symbol ? symbol->getAsFunction() : 0;
     if (prevDec) {
         if (prevDec->getType() != function.getType()) {
-            error(loc, "overloaded functions must have the same return type", function.getType().getCompleteTypeString().c_str(), "");
+            error(loc, "overloaded functions must have the same return type", function.getType().getBasicTypeString().c_str(), "");
         }
         for (int i = 0; i < prevDec->getParamCount(); ++i) {
             if ((*prevDec)[i].type->getQualifier().storage != function[i].type->getQualifier().storage)
@@ -783,7 +783,7 @@ TIntermAggregate* TParseContext::handleFunctionPrototype(TSourceLoc loc, TFuncti
         if (function.getParamCount() > 0)
             error(loc, "function cannot take any parameter(s)", function.getName().c_str(), "");
         if (function.getType().getBasicType() != EbtVoid)
-            error(loc, "", function.getType().getCompleteTypeString().c_str(), "main function cannot return a value");
+            error(loc, "", function.getType().getBasicTypeString().c_str(), "main function cannot return a value");
         intermediate.addMainCount();
     }
 
@@ -1495,23 +1495,15 @@ void TParseContext::boolCheck(TSourceLoc loc, const TPublicType& pType)
         error(loc, "boolean expression expected", "", "");
 }
 
-bool TParseContext::samplerErrorCheck(TSourceLoc loc, const TPublicType& pType, const char* reason)
+void TParseContext::samplerCheck(TSourceLoc loc, const TType& type, const TString& identifier)
 {
-    if (pType.basicType == EbtStruct) {
-        if (containsSampler(*pType.userDef)) {
-            error(loc, reason, TType::getBasicString(pType.basicType), "(structure cannot contain a sampler or image)");
-
-            return true;
-        }
-
-        return false;
-    } else if (pType.basicType == EbtSampler) {
-        error(loc, reason, TType::getBasicString(pType.basicType), "");
-
-        return true;
-    }
+    if (type.getQualifier().storage == EvqUniform)
+        return;
 
-    return false;
+    if (type.getBasicType() == EbtStruct && containsSampler(type))
+        error(loc, "non-uniform struct contains a sampler or image:", type.getBasicTypeString().c_str(), identifier.c_str());
+    else if (type.getBasicType() == EbtSampler && type.getQualifier().storage != EvqUniform)
+        error(loc, "sampler/image types can only be used in uniform variables or function parameters:", type.getBasicTypeString().c_str(), identifier.c_str());
 }
 
 //
@@ -1544,11 +1536,6 @@ void TParseContext::globalQualifierCheck(TSourceLoc loc, const TQualifier& quali
     if (! symbolTable.atGlobalLevel())
         return;
 
-    // Do non-in/out error checks
-
-    if (qualifier.storage != EvqUniform && samplerErrorCheck(loc, publicType, "samplers and images must be uniform"))
-        return;
-
     if (qualifier.storage != EvqVaryingIn && qualifier.storage != EvqVaryingOut)
         return;
 
@@ -1738,7 +1725,7 @@ void TParseContext::precisionQualifierCheck(TSourceLoc loc, TPublicType& publicT
 void TParseContext::parameterSamplerCheck(TSourceLoc loc, TStorageQualifier qualifier, const TType& type)
 {
     if ((qualifier == EvqOut || qualifier == EvqInOut) && type.getBasicType() != EbtStruct && type.getBasicType() == EbtSampler)
-        error(loc, "samplers cannot be output parameters", type.getCompleteTypeString().c_str(), "");
+        error(loc, "samplers cannot be output parameters", type.getBasicTypeString().c_str(), "");
 }
 
 bool TParseContext::containsSampler(const TType& type)
@@ -2147,28 +2134,41 @@ bool TParseContext::redeclareBuiltinBlock(TSourceLoc loc, TTypeList& typeList, c
     return true;
 }
 
-void TParseContext::paramCheck(TSourceLoc loc, const TStorageQualifier& qualifier, TType* type)
+void TParseContext::paramCheckFix(TSourceLoc loc, const TStorageQualifier& qualifier, TType& type)
 {
     switch (qualifier) {
     case EvqConst:
     case EvqConstReadOnly:
-        type->getQualifier().storage = EvqConstReadOnly;
+        type.getQualifier().storage = EvqConstReadOnly;
         break;
     case EvqIn:
     case EvqOut:
     case EvqInOut:
-        type->getQualifier().storage = qualifier;
+        type.getQualifier().storage = qualifier;
         break;
     case EvqTemporary:
-        type->getQualifier().storage = EvqIn;
+        type.getQualifier().storage = EvqIn;
         break;
     default:
-        type->getQualifier().storage = EvqIn;
-        error(loc, "qualifier not allowed on function parameter", GetStorageQualifierString(qualifier), "");
+        type.getQualifier().storage = EvqIn;
+        error(loc, "storage qualifier not allowed on function parameter", GetStorageQualifierString(qualifier), "");
         break;
     }
 }
 
+void TParseContext::paramCheckFix(TSourceLoc loc, const TQualifier& qualifier, TType& type)
+{
+    if (qualifier.isAuxiliary() ||
+        qualifier.isInterpolation())
+        error(loc, "cannot use auxiliary or interpolation qualifiers on a function parameter", "", "");
+    if (qualifier.hasLayout())
+        error(loc, "cannot use layout qualifiers on a function parameter", "", "");
+    if (qualifier.invariant)
+        error(loc, "cannot use invariant qualifier on a function parameter", "", "");    
+
+    paramCheckFix(loc, qualifier.storage, type);
+}
+
 void TParseContext::nestedBlockCheck(TSourceLoc loc)
 {
     if (structNestingLevel > 0)
@@ -2192,6 +2192,33 @@ void TParseContext::arrayObjectCheck(TSourceLoc loc, const TType& type, const ch
     }
 }
 
+void TParseContext::opaqueCheck(TSourceLoc loc, const TType& type, const char* op)
+{
+    if (containsSampler(type))
+        error(loc, "can't use with samplers or structs containing samplers", op, "");
+}
+
+void TParseContext::structTypeCheck(TSourceLoc loc, TPublicType& publicType)
+{
+    TTypeList& typeList = *publicType.userDef->getStruct();
+
+    // fix and check for member storage qualifiers and types that don't belong within a structure
+    for (unsigned int member = 0; member < typeList.size(); ++member) {
+        TQualifier& memberQualifier = typeList[member].type->getQualifier();        
+        TSourceLoc memberLoc = typeList[member].loc;
+        if (memberQualifier.isAuxiliary() ||
+            memberQualifier.isInterpolation() ||
+            (memberQualifier.storage != EvqTemporary && memberQualifier.storage != EvqGlobal))
+            error(memberLoc, "cannot use storage or interpolation qualifiers on structure members", typeList[member].type->getFieldName().c_str(), "");
+        if (memberQualifier.isMemory())
+            error(memberLoc, "cannot use memory qualifiers on structure members", typeList[member].type->getFieldName().c_str(), "");
+        if (memberQualifier.hasLayout())
+            error(memberLoc, "cannot use layout qualifiers on structure members", typeList[member].type->getFieldName().c_str(), "");
+        if (memberQualifier.invariant)
+            error(memberLoc, "cannot use invariant qualifier on structure members", typeList[member].type->getFieldName().c_str(), "");
+    }
+}
+
 //
 // See if this loop satisfies the limitations for ES 2.0 (version 100) for loops in Appendex A:
 //
@@ -2738,6 +2765,9 @@ TIntermNode* TParseContext::declareVariable(TSourceLoc loc, TString& identifier,
     if (! initializer)
         nonInitConstCheck(loc, identifier, type);
 
+    invariantCheck(loc, type, identifier);
+    samplerCheck(loc, type, identifier);
+
     // Pick up defaults
     if (! type.getQualifier().hasStream() && language == EShLangGeometry && type.getQualifier().storage == EvqVaryingOut)
         type.getQualifier().layoutStream = globalOutputDefaults.layoutStream;
@@ -2775,6 +2805,9 @@ TIntermNode* TParseContext::declareVariable(TSourceLoc loc, TString& identifier,
             error(loc, "cannot change the type of", "redeclaration", symbol->getName().c_str());
     }
 
+    if (! symbol)
+        return 0;
+
     // Deal with initializer
     TIntermNode* initNode = 0;
     if (symbol && initializer) {
@@ -2787,11 +2820,10 @@ TIntermNode* TParseContext::declareVariable(TSourceLoc loc, TString& identifier,
     }
 
     // look for errors/adjustments in layout qualifier use
-    if (symbol)
-        layoutTypeCheck(loc, *symbol);
+    layoutTypeCheck(loc, *symbol);
 
     // see if it's a linker-level object to track
-    if (symbol && newDeclaration && symbolTable.atGlobalLevel())
+    if (newDeclaration && symbolTable.atGlobalLevel())
         intermediate.addSymbolLinkageNode(linkage, *symbol);
 
     return initNode;
@@ -3339,8 +3371,10 @@ void TParseContext::addQualifierToExisting(TSourceLoc loc, TQualifier qualifier,
     if (symbol->isReadOnly())
         symbol = symbolTable.copyUp(symbol);
 
-    if (qualifier.invariant)
+    if (qualifier.invariant) {
         symbol->getWritableType().getQualifier().invariant = true;
+        invariantCheck(loc, symbol->getType(), identifier);
+    }
 }
 
 void TParseContext::addQualifierToExisting(TSourceLoc loc, TQualifier qualifier, TIdentifierList& identifiers)
@@ -3349,6 +3383,22 @@ void TParseContext::addQualifierToExisting(TSourceLoc loc, TQualifier qualifier,
         addQualifierToExisting(loc, qualifier, *identifiers[i]);
 }
 
+void TParseContext::invariantCheck(TSourceLoc loc, const TType& type, const TString& identifier)
+{
+    if (! type.getQualifier().invariant)
+        return;
+
+    bool pipeOut = type.getQualifier().isPipeOutput();
+    bool pipeIn = type.getQualifier().isPipeInput();
+    if (version >= 300 || profile != EEsProfile && version >= 420) {
+        if (! pipeOut)
+            error(loc, "can only apply to an output\n", "invariant", identifier.c_str());
+    } else {
+        if ((language == EShLangVertex && pipeIn) || (! pipeOut && ! pipeIn))
+            error(loc, "can only apply to an output or an input in a non-vertex stage\n", "invariant", "");
+    }
+}
+
 //
 // Updating default qualifier for the case of a declaration with just a qualifier,
 // no type, block, or identifier.
diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h
index 7ff225d73054961a6354758956855183b0aa479c..6ae6456910feabbcd9130b3aec03e601fc449b93 100644
--- a/glslang/MachineIndependent/ParseHelper.h
+++ b/glslang/MachineIndependent/ParseHelper.h
@@ -111,7 +111,7 @@ public:
     bool voidErrorCheck(TSourceLoc, const TString&, TBasicType);
     void boolCheck(TSourceLoc, const TIntermTyped*);
     void boolCheck(TSourceLoc, const TPublicType&);
-    bool samplerErrorCheck(TSourceLoc, const TPublicType& pType, const char* reason);
+    void samplerCheck(TSourceLoc, const TType&, const TString& identifier);
     void pipeInOutFix(TSourceLoc, TQualifier&);
     void globalQualifierCheck(TSourceLoc, const TQualifier&, const TPublicType&);
     bool structQualifierErrorCheck(TSourceLoc, const TPublicType& pType);
@@ -124,10 +124,13 @@ public:
     bool containsSampler(const TType& type);
     TSymbol* redeclareBuiltinVariable(TSourceLoc, const TString&, const TQualifier&, const TShaderQualifiers&, bool& newDeclaration);
     bool redeclareBuiltinBlock(TSourceLoc, TTypeList& typeList, const TString& blockName, const TString* instanceName, TArraySizes* arraySizes);
-    void paramCheck(TSourceLoc, const TStorageQualifier&, TType* type);
+    void paramCheckFix(TSourceLoc, const TStorageQualifier&, TType& type);
+    void paramCheckFix(TSourceLoc, const TQualifier&, TType& type);
     void nestedBlockCheck(TSourceLoc);
     void nestedStructCheck(TSourceLoc);
     void arrayObjectCheck(TSourceLoc, const TType&, const char* op);
+    void opaqueCheck(TSourceLoc, const TType&, const char* op);
+    void structTypeCheck(TSourceLoc, TPublicType&);
     void inductiveLoopCheck(TSourceLoc, TIntermNode* init, TIntermLoop* loop);
     void inductiveLoopBodyCheck(TIntermNode*, int loopIndexId, TSymbolTable&);
     void constantIndexExpressionCheck(TIntermNode*);
@@ -151,6 +154,7 @@ public:
     void declareBlock(TSourceLoc, TTypeList& typeList, const TString* instanceName = 0, TArraySizes* arraySizes = 0);
     void addQualifierToExisting(TSourceLoc, TQualifier, const TString& identifier);
     void addQualifierToExisting(TSourceLoc, TQualifier, TIdentifierList&);
+    void invariantCheck(TSourceLoc, const TType&, const TString& identifier);
     void updateStandaloneQualifierDefaults(TSourceLoc, const TPublicType&);
     void updateTypedDefaults(TSourceLoc, const TQualifier&, const TString* id);
     void wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode);
diff --git a/glslang/MachineIndependent/SymbolTable.cpp b/glslang/MachineIndependent/SymbolTable.cpp
index 2977a2e5128b9763229ca59ba3b86f414df205b7..4c6cbcdee956ab7f7b92e7ebbf6c8dcf2253d466 100644
--- a/glslang/MachineIndependent/SymbolTable.cpp
+++ b/glslang/MachineIndependent/SymbolTable.cpp
@@ -136,7 +136,7 @@ int TType::getStructSize() const
 
 void TVariable::dump(TInfoSink& infoSink) const
 {
-    infoSink.debug << getName().c_str() << ": " << type.getStorageQualifierString() << " " << type.getCompleteTypeString();
+    infoSink.debug << getName().c_str() << ": " << type.getStorageQualifierString() << " " << type.getBasicTypeString();
     if (type.isArray()) {
         infoSink.debug << "[0]";
     }
@@ -145,7 +145,7 @@ void TVariable::dump(TInfoSink& infoSink) const
 
 void TFunction::dump(TInfoSink& infoSink) const
 {
-    infoSink.debug << getName().c_str() << ": " <<  returnType.getCompleteTypeString() << " " << getMangledName().c_str() << "\n";
+    infoSink.debug << getName().c_str() << ": " <<  returnType.getBasicTypeString() << " " << getMangledName().c_str() << "\n";
 }
 
 void TAnonMember::dump(TInfoSink& TInfoSink) const
diff --git a/glslang/MachineIndependent/glslang.y b/glslang/MachineIndependent/glslang.y
index cd1966a27043c8e528f8ca5c65744f84293f6fa5..bc7d0e278418fe8ba48806b613ee29260587690e 100644
--- a/glslang/MachineIndependent/glslang.y
+++ b/glslang/MachineIndependent/glslang.y
@@ -552,6 +552,7 @@ equality_expression
     : relational_expression { $$ = $1; }
     | equality_expression EQ_OP relational_expression  {
         parseContext.arrayObjectCheck($2.loc, $1->getType(), "array comparison");
+        parseContext.opaqueCheck($2.loc, $1->getType(), "==");
         $$ = parseContext.intermediate.addBinaryMath(EOpEqual, $1, $3, $2.loc);
         if ($$ == 0) {
             parseContext.binaryOpError($2.loc, "==", $1->getCompleteString(), $3->getCompleteString());
@@ -562,6 +563,7 @@ equality_expression
     }
     | equality_expression NE_OP relational_expression {
         parseContext.arrayObjectCheck($2.loc, $1->getType(), "array comparison");
+        parseContext.opaqueCheck($2.loc, $1->getType(), "!=");
         $$ = parseContext.intermediate.addBinaryMath(EOpNotEqual, $1, $3, $2.loc);
         if ($$ == 0) {
             parseContext.binaryOpError($2.loc, "!=", $1->getCompleteString(), $3->getCompleteString());
@@ -664,6 +666,7 @@ assignment_expression
     : conditional_expression { $$ = $1; }
     | unary_expression assignment_operator assignment_expression {
         parseContext.arrayObjectCheck($2.loc, $1->getType(), "array assignment");
+        parseContext.opaqueCheck($2.loc, $1->getType(), "=");
         parseContext.lValueErrorCheck($2.loc, "assign", $1);
         $$ = parseContext.intermediate.addAssign($2.op, $1, $3, $2.loc);
         if ($$ == 0) {
@@ -921,13 +924,13 @@ parameter_declaration
         
         parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers);
         parseContext.parameterSamplerCheck($2.loc, $1.qualifier.storage, *$$.param.type);
-        parseContext.paramCheck($1.loc, $1.qualifier.storage, $$.param.type);
+        parseContext.paramCheckFix($1.loc, $1.qualifier, *$$.param.type);
     }
     | parameter_declarator {
         $$ = $1;
 
         parseContext.parameterSamplerCheck($1.loc, EvqIn, *$1.param.type);
-        parseContext.paramCheck($1.loc, EvqTemporary, $$.param.type);
+        parseContext.paramCheckFix($1.loc, EvqTemporary, *$$.param.type);
     }
     //
     // Without name
@@ -939,13 +942,13 @@ parameter_declaration
         
         parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers);
         parseContext.parameterSamplerCheck($2.loc, $1.qualifier.storage, *$$.param.type);
-        parseContext.paramCheck($1.loc, $1.qualifier.storage, $$.param.type);
+        parseContext.paramCheckFix($1.loc, $1.qualifier, *$$.param.type);
     }
     | parameter_type_specifier {
         $$ = $1;
 
         parseContext.parameterSamplerCheck($1.loc, EvqIn, *$1.param.type);
-        parseContext.paramCheck($1.loc, EvqTemporary, $$.param.type);
+        parseContext.paramCheckFix($1.loc, EvqTemporary, *$$.param.type);
     }
     ;
 
@@ -1015,6 +1018,7 @@ fully_specified_type
     : type_specifier {
         $$ = $1;
 
+        parseContext.globalQualifierCheck($1.loc, $1.qualifier, $$);
         if ($1.arraySizes) {
             parseContext.profileRequires($1.loc, ENoProfile, 120, GL_3DL_array_objects, "arrayed type");
             parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "arrayed type");
@@ -1950,6 +1954,7 @@ type_specifier_nonarray
     | struct_specifier {
         $$ = $1;
         $$.qualifier.storage = parseContext.symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
+        parseContext.structTypeCheck($$.loc, $$);
     }
     | TYPE_NAME {
         //