Initializer cleanup

Cleaned up the initialer methods in the parser
and added first-class array initializers.

Passes all WebGL tests.

Change-Id: Ia73db8bfd461f36b717444a8ba4c9ec77d1cee36
Reviewed-on: https://swiftshader-review.googlesource.com/3473
Reviewed-by: Nicolas Capens <capn@google.com>
Tested-by: Alexis Hétu <sugoi@google.com>
diff --git a/src/OpenGL/compiler/ParseHelper.cpp b/src/OpenGL/compiler/ParseHelper.cpp
index 8088cbe..a9dbef6 100644
--- a/src/OpenGL/compiler/ParseHelper.cpp
+++ b/src/OpenGL/compiler/ParseHelper.cpp
@@ -556,9 +556,9 @@
 //
 // returns true in case of an error
 //
-bool TParseContext::voidErrorCheck(int line, const TString& identifier, const TPublicType& pubType)
+bool TParseContext::voidErrorCheck(int line, const TString& identifier, const TBasicType& type)
 {
-    if (pubType.type == EbtVoid) {
+    if(type == EbtVoid) {
         error(line, "illegal use of type 'void'", identifier.c_str());
         return true;
     }
@@ -654,6 +654,60 @@
     return false;
 }
 
+// These checks are common for all declarations starting a declarator list, and declarators that follow an empty
+// declaration.
+//
+bool TParseContext::singleDeclarationErrorCheck(const TPublicType &publicType, const TSourceLoc &identifierLocation)
+{
+	switch(publicType.qualifier)
+	{
+	case EvqVaryingIn:
+	case EvqVaryingOut:
+	case EvqAttribute:
+	case EvqVertexIn:
+	case EvqFragmentOut:
+		if(publicType.type == EbtStruct)
+		{
+			error(identifierLocation, "cannot be used with a structure",
+				getQualifierString(publicType.qualifier));
+			return true;
+		}
+
+	default: break;
+	}
+
+	if(publicType.qualifier != EvqUniform && samplerErrorCheck(identifierLocation, publicType,
+		"samplers must be uniform"))
+	{
+		return true;
+	}
+
+	// check for layout qualifier issues
+	const TLayoutQualifier layoutQualifier = publicType.layoutQualifier;
+
+	if(layoutQualifier.matrixPacking != EmpUnspecified)
+	{
+		error(identifierLocation, "layout qualifier", getMatrixPackingString(layoutQualifier.matrixPacking),
+			"only valid for interface blocks");
+		return true;
+	}
+
+	if(layoutQualifier.blockStorage != EbsUnspecified)
+	{
+		error(identifierLocation, "layout qualifier", getBlockStorageString(layoutQualifier.blockStorage),
+			"only valid for interface blocks");
+		return true;
+	}
+
+	if(publicType.qualifier != EvqVertexIn && publicType.qualifier != EvqFragmentOut &&
+		layoutLocationErrorCheck(identifierLocation, publicType.layoutQualifier))
+	{
+		return true;
+	}
+
+	return false;
+}
+
 bool TParseContext::layoutLocationErrorCheck(const TSourceLoc &location, const TLayoutQualifier &layoutQualifier)

 {

 	if(layoutQualifier.location != -1)

@@ -665,6 +719,17 @@
 	return false;

 }

 
+bool TParseContext::locationDeclaratorListCheck(const TSourceLoc& line, const TPublicType &pType)
+{
+	if(pType.layoutQualifier.location != -1)
+	{
+		error(line, "location must only be specified for a single input or output variable", "location");
+		return true;
+	}
+
+	return false;
+}
+
 bool TParseContext::parameterSamplerErrorCheck(int line, TQualifier qualifier, const TType& type)
 {
     if ((qualifier == EvqOut || qualifier == EvqInOut) &&
@@ -834,7 +899,7 @@
             variable->getType().setArraySize(type.arraySize);
     }
 
-    if (voidErrorCheck(line, identifier, type))
+    if (voidErrorCheck(line, identifier, type.type))
         return true;
 
     return false;
@@ -921,24 +986,74 @@
 //
 // Returns true if there was an error.
 //
-bool TParseContext::nonInitErrorCheck(int line, TString& identifier, TPublicType& type, TVariable*& variable)
+bool TParseContext::nonInitErrorCheck(int line, const TString& identifier, TPublicType& type)
 {
-    if (reservedErrorCheck(line, identifier))
-        recover();
+	if(type.qualifier == EvqConstExpr)
+	{
+		// Make the qualifier make sense.
+		type.qualifier = EvqTemporary;
 
-    variable = new TVariable(&identifier, TType(type));
+		// Generate informative error messages for ESSL1.
+		// In ESSL3 arrays and structures containing arrays can be constant.
+		if(shaderVersion < 300 && type.isStructureContainingArrays())
+		{
+			error(line,
+				"structures containing arrays may not be declared constant since they cannot be initialized",
+				identifier.c_str());
+		}
+		else
+		{
+			error(line, "variables with qualifier 'const' must be initialized", identifier.c_str());
+		}
 
-    if (! symbolTable.declare(*variable)) {
-        error(line, "redefinition", variable->getName().c_str());
-        delete variable;
-        variable = 0;
-        return true;
-    }
+		return true;
+	}
+	if(type.isUnsizedArray())
+	{
+		error(line, "implicitly sized arrays need to be initialized", identifier.c_str());
+		return true;
+	}
+	return false;
+}
 
-    if (voidErrorCheck(line, identifier, type))
-        return true;
+// Do some simple checks that are shared between all variable declarations,
+// and update the symbol table.
+//
+// Returns true if declaring the variable succeeded.
+//
+bool TParseContext::declareVariable(const TSourceLoc &line, const TString &identifier, const TType &type,
+	TVariable **variable)
+{
+	ASSERT((*variable) == nullptr);
 
-    return false;
+	// gl_LastFragData may be redeclared with a new precision qualifier
+	if(type.isArray() && identifier.compare(0, 15, "gl_LastFragData") == 0)
+	{
+		const TVariable *maxDrawBuffers =
+			static_cast<const TVariable *>(symbolTable.findBuiltIn("gl_MaxDrawBuffers", shaderVersion));
+		if(type.getArraySize() != maxDrawBuffers->getConstPointer()->getIConst())
+		{
+			error(line, "redeclaration of gl_LastFragData with size != gl_MaxDrawBuffers", identifier.c_str());
+			return false;
+		}
+	}
+
+	if(reservedErrorCheck(line, identifier))
+		return false;
+
+	(*variable) = new TVariable(&identifier, type);
+	if(!symbolTable.declare(**variable))
+	{
+		error(line, "redefinition", identifier.c_str());
+		delete (*variable);
+		(*variable) = nullptr;
+		return false;
+	}
+
+	if(voidErrorCheck(line, identifier, type.getBasicType()))
+		return false;
+
+	return true;
 }
 
 bool TParseContext::paramErrorCheck(int line, TQualifier qualifier, TQualifier paramQualifier, TType* type)
@@ -1028,6 +1143,64 @@
 //
 /////////////////////////////////////////////////////////////////////////////////
 
+const TVariable *TParseContext::getNamedVariable(const TSourceLoc &location,
+	const TString *name,
+	const TSymbol *symbol)
+{
+	const TVariable *variable = NULL;
+
+	if(!symbol)
+	{
+		error(location, "undeclared identifier", name->c_str());
+		recover();
+	}
+	else if(!symbol->isVariable())
+	{
+		error(location, "variable expected", name->c_str());
+		recover();
+	}
+	else
+	{
+		variable = static_cast<const TVariable*>(symbol);
+
+		if(symbolTable.findBuiltIn(variable->getName(), shaderVersion))
+		{
+			recover();
+		}
+
+		// Reject shaders using both gl_FragData and gl_FragColor
+		TQualifier qualifier = variable->getType().getQualifier();
+		if(qualifier == EvqFragData)
+		{
+			mUsesFragData = true;
+		}
+		else if(qualifier == EvqFragColor)
+		{
+			mUsesFragColor = true;
+		}
+
+		// This validation is not quite correct - it's only an error to write to
+		// both FragData and FragColor. For simplicity, and because users shouldn't
+		// be rewarded for reading from undefined varaibles, return an error
+		// if they are both referenced, rather than assigned.
+		if(mUsesFragData && mUsesFragColor)
+		{
+			error(location, "cannot use both gl_FragData and gl_FragColor", name->c_str());
+			recover();
+		}
+	}
+
+	if(!variable)
+	{
+		TType type(EbtFloat, EbpUndefined);
+		TVariable *fakeVariable = new TVariable(name, type);
+		symbolTable.declare(*fakeVariable);
+		variable = fakeVariable;
+	}
+
+	return variable;
+}
+
 //
 // Look up a function name in the symbol table, and make sure it is a function.
 //
@@ -1059,7 +1232,7 @@
 // Initializers show up in several places in the grammar.  Have one set of
 // code to handle them here.
 //
-bool TParseContext::executeInitializer(TSourceLoc line, TString& identifier, TPublicType& pType,
+bool TParseContext::executeInitializer(TSourceLoc line, const TString& identifier, const TPublicType& pType,
                                        TIntermTyped* initializer, TIntermNode*& intermNode, TVariable* variable)
 {
     TType type = TType(pType);
@@ -1068,7 +1241,7 @@
         if (reservedErrorCheck(line, identifier))
             return true;
 
-        if (voidErrorCheck(line, identifier, pType))
+        if (voidErrorCheck(line, identifier, pType.type))
             return true;
 
         //
@@ -1235,6 +1408,360 @@
 	return returnType;
 }
 
+TIntermAggregate *TParseContext::parseSingleDeclaration(TPublicType &publicType,
+	const TSourceLoc &identifierOrTypeLocation,
+	const TString &identifier)
+{
+	TIntermSymbol *symbol = intermediate.addSymbol(0, identifier, TType(publicType), identifierOrTypeLocation);
+
+	bool emptyDeclaration = (identifier == "");
+
+	mDeferredSingleDeclarationErrorCheck = emptyDeclaration;
+
+	if(emptyDeclaration)
+	{
+		if(publicType.isUnsizedArray())
+		{
+			// ESSL3 spec section 4.1.9: Array declaration which leaves the size unspecified is an error.
+			// It is assumed that this applies to empty declarations as well.
+			error(identifierOrTypeLocation, "empty array declaration needs to specify a size", identifier.c_str());
+		}
+	}
+	else
+	{
+		if(singleDeclarationErrorCheck(publicType, identifierOrTypeLocation))
+			recover();
+
+		if(nonInitErrorCheck(identifierOrTypeLocation, identifier, publicType))
+			recover();
+
+		TVariable *variable = nullptr;
+		if(!declareVariable(identifierOrTypeLocation, identifier, TType(publicType), &variable))
+			recover();
+
+		if(variable && symbol)
+			symbol->setId(variable->getUniqueId());
+	}
+
+	return intermediate.makeAggregate(symbol, identifierOrTypeLocation);
+}
+
+TIntermAggregate *TParseContext::parseSingleArrayDeclaration(TPublicType &publicType,
+	const TSourceLoc &identifierLocation,
+	const TString &identifier,
+	const TSourceLoc &indexLocation,
+	TIntermTyped *indexExpression)
+{
+	mDeferredSingleDeclarationErrorCheck = false;
+
+	if(singleDeclarationErrorCheck(publicType, identifierLocation))
+		recover();
+
+	if(nonInitErrorCheck(identifierLocation, identifier, publicType))
+		recover();
+
+	if(arrayTypeErrorCheck(indexLocation, publicType) || arrayQualifierErrorCheck(indexLocation, publicType))
+	{
+		recover();
+	}
+
+	TType arrayType(publicType);
+
+	int size;
+	if(arraySizeErrorCheck(identifierLocation, indexExpression, size))
+	{
+		recover();
+	}
+	// Make the type an array even if size check failed.
+	// This ensures useless error messages regarding the variable's non-arrayness won't follow.
+	arrayType.setArraySize(size);
+
+	TVariable *variable = nullptr;
+	if(!declareVariable(identifierLocation, identifier, arrayType, &variable))
+		recover();
+
+	TIntermSymbol *symbol = intermediate.addSymbol(0, identifier, arrayType, identifierLocation);
+	if(variable && symbol)
+		symbol->setId(variable->getUniqueId());
+
+	return intermediate.makeAggregate(symbol, identifierLocation);
+}
+
+TIntermAggregate *TParseContext::parseSingleInitDeclaration(const TPublicType &publicType,
+	const TSourceLoc &identifierLocation,
+	const TString &identifier,
+	const TSourceLoc &initLocation,
+	TIntermTyped *initializer)
+{
+	mDeferredSingleDeclarationErrorCheck = false;
+
+	if(singleDeclarationErrorCheck(publicType, identifierLocation))
+		recover();
+
+	TIntermNode *intermNode = nullptr;
+	if(!executeInitializer(identifierLocation, identifier, publicType, initializer, intermNode))
+	{
+		//
+		// Build intermediate representation
+		//
+		return intermNode ? intermediate.makeAggregate(intermNode, initLocation) : nullptr;
+	}
+	else
+	{
+		recover();
+		return nullptr;
+	}
+}
+
+TIntermAggregate *TParseContext::parseSingleArrayInitDeclaration(TPublicType &publicType,
+	const TSourceLoc &identifierLocation,
+	const TString &identifier,
+	const TSourceLoc &indexLocation,
+	TIntermTyped *indexExpression,
+	const TSourceLoc &initLocation,
+	TIntermTyped *initializer)
+{
+	mDeferredSingleDeclarationErrorCheck = false;
+
+	if(singleDeclarationErrorCheck(publicType, identifierLocation))
+		recover();
+
+	if(arrayTypeErrorCheck(indexLocation, publicType) || arrayQualifierErrorCheck(indexLocation, publicType))
+	{
+		recover();
+	}
+
+	TPublicType arrayType(publicType);
+
+	int size = 0;
+	// If indexExpression is nullptr, then the array will eventually get its size implicitly from the initializer.
+	if(indexExpression != nullptr && arraySizeErrorCheck(identifierLocation, indexExpression, size))
+	{
+		recover();
+	}
+	// Make the type an array even if size check failed.
+	// This ensures useless error messages regarding the variable's non-arrayness won't follow.
+	arrayType.setArray(true, size);
+
+	// initNode will correspond to the whole of "type b[n] = initializer".
+	TIntermNode *initNode = nullptr;
+	if(!executeInitializer(identifierLocation, identifier, arrayType, initializer, initNode))
+	{
+		return initNode ? intermediate.makeAggregate(initNode, initLocation) : nullptr;
+	}
+	else
+	{
+		recover();
+		return nullptr;
+	}
+}
+
+TIntermAggregate *TParseContext::parseInvariantDeclaration(const TSourceLoc &invariantLoc,
+	const TSourceLoc &identifierLoc,
+	const TString *identifier,
+	const TSymbol *symbol)
+{
+	// invariant declaration
+	if(globalErrorCheck(invariantLoc, symbolTable.atGlobalLevel(), "invariant varying"))
+	{
+		recover();
+	}
+
+	if(!symbol)
+	{
+		error(identifierLoc, "undeclared identifier declared as invariant", identifier->c_str());
+		recover();
+		return nullptr;
+	}
+	else
+	{
+		const TString kGlFrontFacing("gl_FrontFacing");
+		if(*identifier == kGlFrontFacing)
+		{
+			error(identifierLoc, "identifier should not be declared as invariant", identifier->c_str());
+			recover();
+			return nullptr;
+		}
+		symbolTable.addInvariantVarying(std::string(identifier->c_str()));
+		const TVariable *variable = getNamedVariable(identifierLoc, identifier, symbol);
+		ASSERT(variable);
+		const TType &type = variable->getType();
+		TIntermSymbol *intermSymbol = intermediate.addSymbol(variable->getUniqueId(),
+			*identifier, type, identifierLoc);
+
+		TIntermAggregate *aggregate = intermediate.makeAggregate(intermSymbol, identifierLoc);
+		aggregate->setOp(EOpInvariantDeclaration);
+		return aggregate;
+	}
+}
+
+TIntermAggregate *TParseContext::parseDeclarator(TPublicType &publicType, TIntermAggregate *aggregateDeclaration,
+	const TSourceLoc &identifierLocation, const TString &identifier)
+{
+	// If the declaration starting this declarator list was empty (example: int,), some checks were not performed.
+	if(mDeferredSingleDeclarationErrorCheck)
+	{
+		if(singleDeclarationErrorCheck(publicType, identifierLocation))
+			recover();
+		mDeferredSingleDeclarationErrorCheck = false;
+	}
+
+	if(locationDeclaratorListCheck(identifierLocation, publicType))
+		recover();
+
+	if(nonInitErrorCheck(identifierLocation, identifier, publicType))
+		recover();
+
+	TVariable *variable = nullptr;
+	if(!declareVariable(identifierLocation, identifier, TType(publicType), &variable))
+		recover();
+
+	TIntermSymbol *symbol = intermediate.addSymbol(0, identifier, TType(publicType), identifierLocation);
+	if(variable && symbol)
+		symbol->setId(variable->getUniqueId());
+
+	return intermediate.growAggregate(aggregateDeclaration, symbol, identifierLocation);
+}
+
+TIntermAggregate *TParseContext::parseArrayDeclarator(TPublicType &publicType, TIntermAggregate *aggregateDeclaration,
+	const TSourceLoc &identifierLocation, const TString &identifier,
+	const TSourceLoc &arrayLocation, TIntermTyped *indexExpression)
+{
+	// If the declaration starting this declarator list was empty (example: int,), some checks were not performed.
+	if(mDeferredSingleDeclarationErrorCheck)
+	{
+		if(singleDeclarationErrorCheck(publicType, identifierLocation))
+			recover();
+		mDeferredSingleDeclarationErrorCheck = false;
+	}
+
+	if(locationDeclaratorListCheck(identifierLocation, publicType))
+		recover();
+
+	if(nonInitErrorCheck(identifierLocation, identifier, publicType))
+		recover();
+
+	if(arrayTypeErrorCheck(arrayLocation, publicType) || arrayQualifierErrorCheck(arrayLocation, publicType))
+	{
+		recover();
+	}
+	else
+	{
+		TType arrayType = TType(publicType);
+		int size;
+		if(arraySizeErrorCheck(arrayLocation, indexExpression, size))
+		{
+			recover();
+		}
+		arrayType.setArraySize(size);
+
+		TVariable *variable = nullptr;
+		if(!declareVariable(identifierLocation, identifier, arrayType, &variable))
+			recover();
+
+		TIntermSymbol *symbol = intermediate.addSymbol(0, identifier, arrayType, identifierLocation);
+		if(variable && symbol)
+			symbol->setId(variable->getUniqueId());
+
+		return intermediate.growAggregate(aggregateDeclaration, symbol, identifierLocation);
+	}
+
+	return nullptr;
+}
+
+TIntermAggregate *TParseContext::parseInitDeclarator(const TPublicType &publicType, TIntermAggregate *aggregateDeclaration,
+	const TSourceLoc &identifierLocation, const TString &identifier,
+	const TSourceLoc &initLocation, TIntermTyped *initializer)
+{
+	// If the declaration starting this declarator list was empty (example: int,), some checks were not performed.
+	if(mDeferredSingleDeclarationErrorCheck)
+	{
+		if(singleDeclarationErrorCheck(publicType, identifierLocation))
+			recover();
+		mDeferredSingleDeclarationErrorCheck = false;
+	}
+
+	if(locationDeclaratorListCheck(identifierLocation, publicType))
+		recover();
+
+	TIntermNode *intermNode = nullptr;
+	if(!executeInitializer(identifierLocation, identifier, publicType, initializer, intermNode))
+	{
+		//
+		// build the intermediate representation
+		//
+		if(intermNode)
+		{
+			return intermediate.growAggregate(aggregateDeclaration, intermNode, initLocation);
+		}
+		else
+		{
+			return aggregateDeclaration;
+		}
+	}
+	else
+	{
+		recover();
+		return nullptr;
+	}
+}
+
+TIntermAggregate *TParseContext::parseArrayInitDeclarator(const TPublicType &publicType,
+	TIntermAggregate *aggregateDeclaration,
+	const TSourceLoc &identifierLocation,
+	const TString &identifier,
+	const TSourceLoc &indexLocation,
+	TIntermTyped *indexExpression,
+	const TSourceLoc &initLocation, TIntermTyped *initializer)
+{
+	// If the declaration starting this declarator list was empty (example: int,), some checks were not performed.
+	if(mDeferredSingleDeclarationErrorCheck)
+	{
+		if(singleDeclarationErrorCheck(publicType, identifierLocation))
+			recover();
+		mDeferredSingleDeclarationErrorCheck = false;
+	}
+
+	if(locationDeclaratorListCheck(identifierLocation, publicType))
+		recover();
+
+	if(arrayTypeErrorCheck(indexLocation, publicType) || arrayQualifierErrorCheck(indexLocation, publicType))
+	{
+		recover();
+	}
+
+	TPublicType arrayType(publicType);
+
+	int size = 0;
+	// If indexExpression is nullptr, then the array will eventually get its size implicitly from the initializer.
+	if(indexExpression != nullptr && arraySizeErrorCheck(identifierLocation, indexExpression, size))
+	{
+		recover();
+	}
+	// Make the type an array even if size check failed.
+	// This ensures useless error messages regarding the variable's non-arrayness won't follow.
+	arrayType.setArray(true, size);
+
+	// initNode will correspond to the whole of "b[n] = initializer".
+	TIntermNode *initNode = nullptr;
+	if(!executeInitializer(identifierLocation, identifier, arrayType, initializer, initNode))
+	{
+		if(initNode)
+		{
+			return intermediate.growAggregate(aggregateDeclaration, initNode, initLocation);
+		}
+		else
+		{
+			return aggregateDeclaration;
+		}
+	}
+	else
+	{
+		recover();
+		return nullptr;
+	}
+}
+
 // This function is used to test for the correctness of the parameters passed to various constructor functions
 // and also convert them to the right datatype if it is allowed and required.
 //
@@ -1952,7 +2479,7 @@
 
 TFieldList *TParseContext::addStructDeclaratorList(const TPublicType &typeSpecifier, TFieldList *fieldList)
 {
-	if(voidErrorCheck(typeSpecifier.line, (*fieldList)[0]->name(), typeSpecifier))
+	if(voidErrorCheck(typeSpecifier.line, (*fieldList)[0]->name(), typeSpecifier.type))
 	{
 		recover();
 	}