--- /dev/null
+#version 300 es
+
+int f(int a, int b, int c)
+{
+ int a = b; // ERROR, redefinition
+
+ {
+ float a = float(a) + 1.0;
+ }
+
+ return a;
+}
+
+int f(int a, int b, int c); // okay to redeclare
+
+bool b;
+float b(int a); // ERROR: redefinition
+
+float f; // ERROR: redefinition
+float tan; // ERROR: redefines built-in function
+float sin(float x); // ERROR: can't overload built-in functions
+float cos(float x) // ERROR: can't overload built-in functions
+{
+ return 1.0;
+}
+
+invariant gl_Position;
+
+void main()
+{
+ int g(); // ERROR: no local function declarations
+ g();
+
+ float sin; // okay
+ sin;
+
+ f(1,2,3);
+
+ float f; // hides f()
+ f = 3.0;
+
+ gl_Position = vec4(f);
+
+ for (int f = 0; f < 10; ++f)
+ ++f;
+
+ int x = 1;
+ {
+ float x = 2.0, /* 2nd x visible here */ y = x; // y is initialized to 2
+ int z = z; // ERROR: z not previously defined.
+ }
+ {
+ int x = x; // x is initialized to '1'
+ }
+
+ struct S
+ {
+ int x;
+ };
+ {
+ S S = S(0); // 'S' is only visible as a struct and constructor
+ S.x; // 'S' is now visible as a variable
+ }
+}
--- /dev/null
+#version 430 core
+
+int f(int a, int b, int c)
+{
+ int a = b; // ERROR, redefinition
+
+ {
+ float a = float(a) + 1.0; // okay
+ }
+
+ return a;
+}
+
+int f(int a, int b, int c); // okay to redeclare
+
+bool b;
+float b(int a); // ERROR: redefinition
+
+float f; // ERROR: redefinition
+float tan; // okay, hides built-in function
+float sin(float x); // okay, can overload built-in functions
+float cos(float x) // okay, can overload built-in functions
+{
+ return 1.0;
+}
+
+invariant gl_Position;
+
+void main()
+{
+ int g(); // okay
+ g();
+
+ float sin; // okay
+ sin;
+
+ f(1,2,3);
+
+ float f; // hides f()
+ f = 3.0;
+
+ gl_Position = vec4(f);
+
+ for (int f = 0; f < 10; ++f)
+ ++f;
+
+ int x = 1;
+ {
+ float x = 2.0, /* 2nd x visible here */ y = x; // y is initialized to 2
+ int z = z; // ERROR: z not previously defined.
+ }
+ {
+ int x = x; // x is initialized to '1'
+ }
+
+ struct S
+ {
+ int x;
+ };
+ {
+ S S = S(0); // 'S' is only visible as a struct and constructor
+ S.x; // 'S' is now visible as a variable
+ }
+}
uint.frag
switch.frag
tokenLength.vert
+300scope.vert
+430scope.vert
++numErrors;
}
+TIntermTyped* TParseContext::handleVariable(int line, TSymbol* symbol, TString* string)
+{
+ TIntermTyped* node = 0;
+
+ TAnonMember* anon = symbol ? symbol->getAsAnonMember() : 0;
+ if (anon) {
+ // it was a member of an anonymous container, have to insert its dereference
+ TVariable* variable = anon->getAnonContainer().getAsVariable();
+ TIntermTyped* container = intermediate.addSymbol(variable->getUniqueId(), variable->getName(), variable->getType(), line);
+ constUnion* unionArray = new constUnion[1];
+ unionArray->setUConst(anon->getMemberNumber());
+ TIntermTyped* constNode = intermediate.addConstantUnion(unionArray, TType(EbtUint, EvqConst), line);
+
+ node = intermediate.addIndex(EOpIndexDirectStruct, container, constNode, line);
+ node->setType(*(*variable->getType().getStruct())[anon->getMemberNumber()].type);
+ } else {
+ // The symbol table search was done in the lexical phase, but
+ // if this is a new symbol, it wouldn't have found it.
+ const TVariable* variable = symbol ? symbol->getAsVariable() : 0;
+ if (symbol && ! variable) {
+ error(line, "variable name expected", string->c_str(), "");
+ recover();
+ }
+
+ if (! variable)
+ variable = new TVariable(string, TType(EbtVoid));
+
+ // don't delete $1.string, it's used by error recovery, and the pool
+ // pop will reclaim the memory
+
+ if (variable->getType().getQualifier().storage == EvqConst ) {
+ constUnion* constArray = variable->getConstUnionPointer();
+ TType t(variable->getType());
+ node = intermediate.addConstantUnion(constArray, t, line);
+ } else
+ node = intermediate.addSymbol(variable->getUniqueId(), variable->getName(), variable->getType(), line);
+ }
+
+ return node;
+}
+
//
// Same error message for all places assignments don't work.
//
// because reserved arrays can be redeclared.
//
- bool builtIn = false;
bool sameScope = false;
- TSymbol* symbol = symbolTable.find(identifier, &builtIn, &sameScope);
+ TSymbol* symbol = symbolTable.find(identifier, 0, &sameScope);
if (symbol == 0 || !sameScope) {
if (reservedErrorCheck(line, identifier))
return true;
bool TParseContext::arraySetMaxSize(TIntermSymbol *node, TType* type, int size, bool updateFlag, TSourceLoc line)
{
- bool builtIn = false;
- TSymbol* symbol = symbolTable.find(node->getSymbol(), &builtIn);
+ TSymbol* symbol = symbolTable.find(node->getSymbol());
if (symbol == 0) {
error(line, " undeclared identifier", node->getSymbol().c_str(), "");
return true;
// special casing to test index value of gl_TexCoord. If the accessed index is >= gl_MaxTextureCoords
// its an error
if (node->getSymbol() == "gl_TexCoord") {
- TSymbol* texCoord = symbolTable.find("gl_MaxTextureCoords", &builtIn);
+ TSymbol* texCoord = symbolTable.find("gl_MaxTextureCoords");
if (! texCoord || ! texCoord->getAsVariable()) {
infoSink.info.message(EPrefixInternalError, "gl_MaxTextureCoords not defined", line);
return true;
bool reservedErrorCheck(int line, const TString& identifier);
void recover();
+ TIntermTyped* handleVariable(int line, TSymbol* symbol, TString* string);
bool parseVectorFields(const TString&, int vecSize, TVectorFields&, int line);
void assignError(int line, const char* op, TString left, TString right);
void unaryOpError(int line, const char* op, TString operand);
return true;
}
-//
-// Driver must call this first, once, before doing any other
-// compiler/linker operations.
-//
-
void SetupBuiltinSymbolTable(int version, EProfile profile)
{
TInfoSink infoSink;
SetGlobalPoolAllocatorPtr(*builtInPoolAllocator);
TSymbolTable symTables[EShLangCount];
+ if (profile == EEsProfile) {
+ for (int stage = 0; stage < EShLangCount; ++stage)
+ symTables[stage].setNoBuiltInRedeclarations();
+ }
+
GenerateBuiltInSymbolTable(infoSink, symTables, version, profile);
SetGlobalPoolAllocatorPtr(*PerProcessGPA);
TSymbolTableLevel *symTableLevel = new TSymbolTableLevel();
symTableLevel->anonId = anonId;
tLevel::iterator iter;
- for (iter = level.begin(); iter != level.end(); ++iter) {
+ for (iter = level.begin(); iter != level.end(); ++iter)
symTableLevel->insert(*iter->second->clone(remapper));
- }
return symTableLevel;
}
{
TStructureMap remapper;
uniqueId = copyOf.uniqueId;
- for (unsigned int i = 0; i < copyOf.table.size(); ++i) {
+ noBuiltInRedeclarations = copyOf.noBuiltInRedeclarations;
+ for (unsigned int i = 0; i < copyOf.table.size(); ++i)
table.push_back(copyOf.table[i]->clone(remapper));
- }
}
bool insert(TSymbol& symbol)
{
//
- // returning true means symbol was added to the table
+ // returning true means symbol was added to the table with no semantic errors
//
tInsertResult result;
- if (symbol.getName() == "") {
+ const TString& name = symbol.getName();
+ if (name == "") {
// An empty name means an anonymous container, exposing its members to the external scope.
// Give it a name and insert its members in the symbol table, pointing to the container.
char buf[20];
return isOkay;
} else {
- result = level.insert(tLevelPair(symbol.getMangledName(), &symbol));
-
- return result.second;
+ // Check for redefinition errors:
+ // - STL itself will tell us if there is a direct name collision at this level
+ // - additionally, check for function/variable name collisions
+ // - for ES, for overriding or hiding built-in function
+ const TString& insertName = symbol.getMangledName();
+ if (symbol.getAsFunction()) {
+ // make sure there isn't a variable of this name
+ if (level.find(name) != level.end())
+ return false;
+
+ // insert, and whatever happens is okay
+ level.insert(tLevelPair(insertName, &symbol));
+
+ return true;
+ } else {
+ result = level.insert(tLevelPair(insertName, &symbol));
+
+ return result.second;
+ }
}
}
return (*it).second;
}
+ bool hasFunctionName(const TString& name) const
+ {
+ tLevel::const_iterator candidate = level.lower_bound(name);
+ if (candidate != level.end()) {
+ const TString& candidateName = (*candidate).first;
+ TString::size_type parenAt = candidateName.find_first_of('(');
+ if (parenAt != candidateName.npos && candidateName.substr(0, parenAt) == name)
+
+ return true;
+ }
+
+ return false;
+ }
+
// Use this to do a lazy 'push' of precision defaults the first time
// a precision statement is seen in a new scope. Leave it at 0 for
// when no push was needed. Thus, it is not the current defaults,
class TSymbolTable {
public:
- TSymbolTable() : uniqueId(0)
+ TSymbolTable() : uniqueId(0), noBuiltInRedeclarations(false)
{
//
// The symbol table cannot be used until push() is called, but
{
table.push_back(symTable.table[0]);
uniqueId = symTable.uniqueId;
+ noBuiltInRedeclarations = symTable.noBuiltInRedeclarations;
}
~TSymbolTable()
{
- // level 0 is always built In symbols, so we never pop that out
+ // level 0 is always built-in symbols, so we never pop that out
while (table.size() > 1)
pop(0);
}
//
// When the symbol table is initialized with the built-ins, there should
- // 'push' calls, so that built-ins are at level 0 and the shader
- // globals are at level 1.
+ // 'push' calls, so that built-in shared across all compiles are at level 0
+ // built-ins specific to a compile are at level 1 and the shader
+ // globals are at level 2.
//
bool isEmpty() { return table.size() == 0; }
bool atBuiltInLevel() { return atSharedBuiltInLevel() || atDynamicBuiltInLevel(); }
bool atSharedBuiltInLevel() { return table.size() == 1; }
bool atGlobalLevel() { return table.size() <= 3; }
+ void setNoBuiltInRedeclarations() { noBuiltInRedeclarations = true; }
void push()
{
bool insert(TSymbol& symbol)
{
symbol.setUniqueId(++uniqueId);
+
+ if (symbol.getAsVariable()) {
+ // make sure there isn't a function of this name
+ if (table[currentLevel()]->hasFunctionName(symbol.getName()))
+ return false;
+ if (atGlobalLevel() && currentLevel() > 0 && noBuiltInRedeclarations) {
+ if (table[0]->hasFunctionName(symbol.getName()))
+ return false;
+ if (currentLevel() > 1 && table[1]->hasFunctionName(symbol.getName()))
+ return false;
+ }
+ }
+
return table[currentLevel()]->insert(symbol);
}
} while (symbol == 0 && level >= 0);
level++;
if (builtIn)
- *builtIn = level == 0;
+ *builtIn = level < 2;
if (sameScope)
*sameScope = level == currentLevel();
return symbol;
std::vector<TSymbolTableLevel*> table;
int uniqueId; // for unique identification in code generation
+ bool noBuiltInRedeclarations;
};
#endif // _SYMBOL_TABLE_INCLUDED_
\r
variable_identifier\r
: IDENTIFIER {\r
- // The symbol table search was done in the lexical phase, but\r
- // if this is a new symbol, it won't find it, which is okay at this\r
- // point in the grammar.\r
- TSymbol* symbol = $1.symbol;\r
- TAnonMember* anon = symbol ? symbol->getAsAnonMember() : 0;\r
- if (anon) {\r
- // it was a member of an anonymous container, have to insert its dereference\r
- TVariable* variable = anon->getAnonContainer().getAsVariable();\r
- TIntermTyped* container = parseContext.intermediate.addSymbol(variable->getUniqueId(),\r
- variable->getName(),\r
- variable->getType(), $1.line);\r
- constUnion* unionArray = new constUnion[1];\r
- unionArray->setUConst(anon->getMemberNumber());\r
- TIntermTyped* constNode = parseContext.intermediate.addConstantUnion(unionArray, TType(EbtUint, EvqConst), $1.line);\r
-\r
- $$ = parseContext.intermediate.addIndex(EOpIndexDirectStruct, container, constNode, $1.line);\r
- $$->setType(*(*variable->getType().getStruct())[anon->getMemberNumber()].type);\r
- } else {\r
- const TVariable* variable = symbol ? symbol->getAsVariable() : 0;\r
- if (symbol && ! variable) {\r
- parseContext.error($1.line, "variable name expected", $1.string->c_str(), "");\r
- parseContext.recover();\r
- }\r
-\r
- if (! variable)\r
- variable = new TVariable($1.string, TType(EbtVoid));\r
-\r
- // don't delete $1.string, it's used by error recovery, and the pool\r
- // pop will reclaim the memory\r
-\r
- if (variable->getType().getQualifier().storage == EvqConst ) {\r
- constUnion* constArray = variable->getConstUnionPointer();\r
- TType t(variable->getType());\r
- $$ = parseContext.intermediate.addConstantUnion(constArray, t, $1.line);\r
- } else\r
- $$ = parseContext.intermediate.addSymbol(variable->getUniqueId(),\r
- variable->getName(),\r
- variable->getType(), $1.line);\r
- }\r
+ $$ = parseContext.handleVariable($1.line, $1.symbol, $1.string);\r
}\r
;\r
\r
\r
function_prototype\r
: function_declarator RIGHT_PAREN {\r
+ // ES can't declare prototypes inside functions\r
+ if (! parseContext.symbolTable.atGlobalLevel())\r
+ parseContext.requireProfile($2.line, static_cast<EProfileMask>(~EEsProfileMask), "local function declaration");\r
+\r
//\r
// Multiple declarations of the same function are allowed.\r
//\r
// If this is a definition, the definition production code will check for redefinitions\r
// (we don't know at this point if it's a definition or not).\r
//\r
- // Redeclarations are allowed. But, return types and parameter qualifiers must match.\r
+ // Redeclarations (full prototype match) are allowed. But, return types and parameter qualifiers must match.\r
//\r
- TSymbol* symbol = parseContext.symbolTable.find($1->getMangledName());\r
+ // ES does not allow redeclaring or hiding of built-in functions.\r
+ //\r
+ bool builtIn;\r
+ TSymbol* symbol = parseContext.symbolTable.find($1->getMangledName(), &builtIn);\r
+ if (symbol && symbol->getAsFunction() && builtIn)\r
+ parseContext.requireProfile($2.line, static_cast<EProfileMask>(~EEsProfileMask), "redeclaration of built-in function");\r
TFunction* prevDec = symbol ? symbol->getAsFunction() : 0;\r
if (prevDec) {\r
if (prevDec->getReturnType() != $1->getReturnType()) {\r
$$.function = $1;\r
$$.line = $2.line;\r
\r
- parseContext.symbolTable.insert(*$$.function);\r
+ if (! parseContext.symbolTable.insert(*$$.function)) {\r
+ parseContext.error($2.line, "illegal redeclaration", $$.function->getName().c_str(), "");\r
+ parseContext.recover();\r
+ }\r
}\r
;\r
\r