452 lines
15 KiB
C++
452 lines
15 KiB
C++
//
|
|
//Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
|
|
//Copyright (C) 2016 Google, Inc.
|
|
//
|
|
//All rights reserved.
|
|
//
|
|
//Redistribution and use in source and binary forms, with or without
|
|
//modification, are permitted provided that the following conditions
|
|
//are met:
|
|
//
|
|
// Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
//
|
|
// Redistributions in binary form must reproduce the above
|
|
// copyright notice, this list of conditions and the following
|
|
// disclaimer in the documentation and/or other materials provided
|
|
// with the distribution.
|
|
//
|
|
// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
|
|
// contributors may be used to endorse or promote products derived
|
|
// from this software without specific prior written permission.
|
|
//
|
|
//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
//POSSIBILITY OF SUCH DAMAGE.
|
|
//
|
|
|
|
// Implement the TParseContextBase class.
|
|
|
|
#include <cstdarg>
|
|
|
|
#include "ParseHelper.h"
|
|
|
|
extern int yyparse(glslang::TParseContext*);
|
|
|
|
namespace glslang {
|
|
|
|
//
|
|
// Used to output syntax, parsing, and semantic errors.
|
|
//
|
|
|
|
void TParseContextBase::outputMessage(const TSourceLoc& loc, const char* szReason,
|
|
const char* szToken,
|
|
const char* szExtraInfoFormat,
|
|
TPrefixType prefix, va_list args)
|
|
{
|
|
const int maxSize = MaxTokenLength + 200;
|
|
char szExtraInfo[maxSize];
|
|
|
|
safe_vsprintf(szExtraInfo, maxSize, szExtraInfoFormat, args);
|
|
|
|
infoSink.info.prefix(prefix);
|
|
infoSink.info.location(loc);
|
|
infoSink.info << "'" << szToken << "' : " << szReason << " " << szExtraInfo << "\n";
|
|
|
|
if (prefix == EPrefixError) {
|
|
++numErrors;
|
|
}
|
|
}
|
|
|
|
void C_DECL TParseContextBase::error(const TSourceLoc& loc, const char* szReason, const char* szToken,
|
|
const char* szExtraInfoFormat, ...)
|
|
{
|
|
if (messages & EShMsgOnlyPreprocessor)
|
|
return;
|
|
va_list args;
|
|
va_start(args, szExtraInfoFormat);
|
|
outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixError, args);
|
|
va_end(args);
|
|
|
|
if ((messages & EShMsgCascadingErrors) == 0)
|
|
currentScanner->setEndOfInput();
|
|
}
|
|
|
|
void C_DECL TParseContextBase::warn(const TSourceLoc& loc, const char* szReason, const char* szToken,
|
|
const char* szExtraInfoFormat, ...)
|
|
{
|
|
if (suppressWarnings())
|
|
return;
|
|
va_list args;
|
|
va_start(args, szExtraInfoFormat);
|
|
outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixWarning, args);
|
|
va_end(args);
|
|
}
|
|
|
|
void C_DECL TParseContextBase::ppError(const TSourceLoc& loc, const char* szReason, const char* szToken,
|
|
const char* szExtraInfoFormat, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, szExtraInfoFormat);
|
|
outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixError, args);
|
|
va_end(args);
|
|
|
|
if ((messages & EShMsgCascadingErrors) == 0)
|
|
currentScanner->setEndOfInput();
|
|
}
|
|
|
|
void C_DECL TParseContextBase::ppWarn(const TSourceLoc& loc, const char* szReason, const char* szToken,
|
|
const char* szExtraInfoFormat, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, szExtraInfoFormat);
|
|
outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixWarning, args);
|
|
va_end(args);
|
|
}
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
// Returns true if there was an error.
|
|
//
|
|
bool TParseContextBase::lValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node)
|
|
{
|
|
TIntermBinary* binaryNode = node->getAsBinaryNode();
|
|
|
|
if (binaryNode) {
|
|
switch(binaryNode->getOp()) {
|
|
case EOpIndexDirect:
|
|
case EOpIndexIndirect: // fall through
|
|
case EOpIndexDirectStruct: // fall through
|
|
case EOpVectorSwizzle:
|
|
return lValueErrorCheck(loc, op, binaryNode->getLeft());
|
|
default:
|
|
break;
|
|
}
|
|
error(loc, " l-value required", op, "", "");
|
|
|
|
return true;
|
|
}
|
|
|
|
const char* symbol = nullptr;
|
|
TIntermSymbol* symNode = node->getAsSymbolNode();
|
|
if (symNode != nullptr)
|
|
symbol = symNode->getName().c_str();
|
|
|
|
const char* message = nullptr;
|
|
switch (node->getQualifier().storage) {
|
|
case EvqConst: message = "can't modify a const"; break;
|
|
case EvqConstReadOnly: message = "can't modify a const"; break;
|
|
case EvqUniform: message = "can't modify a uniform"; break;
|
|
case EvqBuffer:
|
|
if (node->getQualifier().readonly)
|
|
message = "can't modify a readonly buffer";
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// Type that can't be written to?
|
|
//
|
|
switch (node->getBasicType()) {
|
|
case EbtSampler:
|
|
message = "can't modify a sampler";
|
|
break;
|
|
case EbtAtomicUint:
|
|
message = "can't modify an atomic_uint";
|
|
break;
|
|
case EbtVoid:
|
|
message = "can't modify void";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (message == nullptr && binaryNode == nullptr && symNode == nullptr) {
|
|
error(loc, " l-value required", op, "", "");
|
|
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Everything else is okay, no error.
|
|
//
|
|
if (message == nullptr)
|
|
return false;
|
|
|
|
//
|
|
// If we get here, we have an error and a message.
|
|
//
|
|
if (symNode)
|
|
error(loc, " l-value required", op, "\"%s\" (%s)", symbol, message);
|
|
else
|
|
error(loc, " l-value required", op, "(%s)", message);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Test for and give an error if the node can't be read from.
|
|
void TParseContextBase::rValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node)
|
|
{
|
|
if (! node)
|
|
return;
|
|
|
|
TIntermBinary* binaryNode = node->getAsBinaryNode();
|
|
if (binaryNode) {
|
|
switch(binaryNode->getOp()) {
|
|
case EOpIndexDirect:
|
|
case EOpIndexIndirect:
|
|
case EOpIndexDirectStruct:
|
|
case EOpVectorSwizzle:
|
|
rValueErrorCheck(loc, op, binaryNode->getLeft());
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
TIntermSymbol* symNode = node->getAsSymbolNode();
|
|
if (symNode && symNode->getQualifier().writeonly)
|
|
error(loc, "can't read from writeonly object: ", op, symNode->getName().c_str());
|
|
}
|
|
|
|
// Make a shared symbol have a non-shared version that can be edited by the current
|
|
// compile, such that editing its type will not change the shared version and will
|
|
// effect all nodes sharing it.
|
|
void TParseContextBase::makeEditable(TSymbol*& symbol)
|
|
{
|
|
// copyUp() does a deep copy of the type.
|
|
symbol = symbolTable.copyUp(symbol);
|
|
|
|
// Save it in the AST for linker use.
|
|
intermediate.addSymbolLinkageNode(linkage, *symbol);
|
|
}
|
|
|
|
// Return a writable version of the variable 'name'.
|
|
//
|
|
// Return nullptr if 'name' is not found. This should mean
|
|
// something is seriously wrong (e.g., compiler asking self for
|
|
// built-in that doesn't exist).
|
|
TVariable* TParseContextBase::getEditableVariable(const char* name)
|
|
{
|
|
bool builtIn;
|
|
TSymbol* symbol = symbolTable.find(name, &builtIn);
|
|
|
|
assert(symbol != nullptr);
|
|
if (symbol == nullptr)
|
|
return nullptr;
|
|
|
|
if (builtIn)
|
|
makeEditable(symbol);
|
|
|
|
return symbol->getAsVariable();
|
|
}
|
|
|
|
// Select the best matching function for 'call' from 'candidateList'.
|
|
//
|
|
// Assumptions
|
|
//
|
|
// There is no exact match, so a selection algorithm needs to run. That is, the
|
|
// language-specific handler should check for exact match first, to
|
|
// decide what to do, before calling this selector.
|
|
//
|
|
// Input
|
|
//
|
|
// * list of candidate signatures to select from
|
|
// * the call
|
|
// * a predicate function convertible(from, to) that says whether or not type
|
|
// 'from' can implicitly convert to type 'to' (it includes the case of what
|
|
// the calling language would consider a matching type with no conversion
|
|
// needed)
|
|
// * a predicate function better(from1, from2, to1, to2) that says whether or
|
|
// not a conversion from <-> to2 is considered better than a conversion
|
|
// from <-> to1 (both in and out directions need testing, as declared by the
|
|
// formal parameter)
|
|
//
|
|
// Output
|
|
//
|
|
// * best matching candidate (or none, if no viable candidates found)
|
|
// * whether there was a tie for the best match (ambiguous overload selection,
|
|
// caller's choice for how to report)
|
|
//
|
|
const TFunction* TParseContextBase::selectFunction(
|
|
const TVector<const TFunction*> candidateList,
|
|
const TFunction& call,
|
|
std::function<bool(const TType& from, const TType& to)> convertible,
|
|
std::function<bool(const TType& from, const TType& to1, const TType& to2)> better,
|
|
/* output */ bool& tie)
|
|
{
|
|
//
|
|
// Operation
|
|
//
|
|
// 1. Prune the input list of candidates down to a list of viable candidates,
|
|
// where each viable candidate has
|
|
//
|
|
// * at least as many parameters as there are calling arguments, with any
|
|
// remaining parameters being optional or having default values
|
|
// * each parameter is true under convertible(A, B), where A is the calling
|
|
// type for in and B is the formal type, and in addition, for out B is the
|
|
// calling type and A is the formal type
|
|
//
|
|
// 2. If there are no viable candidates, return with no match.
|
|
//
|
|
// 3. If there is only one viable candidate, it is the best match.
|
|
//
|
|
// 4. If there are multiple viable candidates, select the first viable candidate
|
|
// as the incumbent. Compare the incumbent to the next viable candidate, and if
|
|
// that candidate is better (bullets below), make it the incumbent. Repeat, with
|
|
// a linear walk through the viable candidate list. The final incumbent will be
|
|
// returned as the best match. A viable candidate is better than the incumbent if
|
|
//
|
|
// * it has a function argument with a better(...) conversion than the incumbent,
|
|
// for all directions needed by in and out
|
|
// * the incumbent has no argument with a better(...) conversion then the
|
|
// candidate, for either in or out (as needed)
|
|
//
|
|
// 5. Check for ambiguity by comparing the best match against all other viable
|
|
// candidates. If any other viable candidate has a function argument with a
|
|
// better(...) conversion than the best candidate (for either in or out
|
|
// directions), return that there was a tie for best.
|
|
//
|
|
|
|
tie = false;
|
|
|
|
// 1. prune to viable...
|
|
TVector<const TFunction*> viableCandidates;
|
|
for (auto it = candidateList.begin(); it != candidateList.end(); ++it) {
|
|
const TFunction& candidate = *(*it);
|
|
|
|
// to even be a potential match, number of arguments has to match
|
|
if (call.getParamCount() != candidate.getParamCount())
|
|
continue;
|
|
|
|
// see if arguments are convertible
|
|
bool viable = true;
|
|
for (int param = 0; param < candidate.getParamCount(); ++param) {
|
|
if (candidate[param].type->getQualifier().isParamInput()) {
|
|
if (! convertible(*call[param].type, *candidate[param].type)) {
|
|
viable = false;
|
|
break;
|
|
}
|
|
}
|
|
if (candidate[param].type->getQualifier().isParamOutput()) {
|
|
if (! convertible(*candidate[param].type, *call[param].type)) {
|
|
viable = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (viable)
|
|
viableCandidates.push_back(&candidate);
|
|
}
|
|
|
|
// 2. none viable...
|
|
if (viableCandidates.size() == 0)
|
|
return nullptr;
|
|
|
|
// 3. only one viable...
|
|
if (viableCandidates.size() == 1)
|
|
return viableCandidates.front();
|
|
|
|
// 4. find best...
|
|
auto betterParam = [&call, &better](const TFunction& can1, const TFunction& can2) -> bool {
|
|
// is call -> can2 better than call -> can1 for any parameter
|
|
bool hasBetterParam = false;
|
|
for (int param = 0; param < call.getParamCount(); ++param) {
|
|
if (better(*call[param].type, *can1[param].type, *can2[param].type)) {
|
|
hasBetterParam = true;
|
|
break;
|
|
}
|
|
}
|
|
return hasBetterParam;
|
|
};
|
|
|
|
const TFunction* incumbent = viableCandidates.front();
|
|
for (auto it = viableCandidates.begin() + 1; it != viableCandidates.end(); ++it) {
|
|
const TFunction& candidate = *(*it);
|
|
if (betterParam(*incumbent, candidate) && ! betterParam(candidate, *incumbent))
|
|
incumbent = &candidate;
|
|
}
|
|
|
|
// 5. ambiguity...
|
|
for (auto it = viableCandidates.begin(); it != viableCandidates.end(); ++it) {
|
|
if (incumbent == *it)
|
|
continue;
|
|
const TFunction& candidate = *(*it);
|
|
if (betterParam(*incumbent, candidate))
|
|
tie = true;
|
|
}
|
|
|
|
return incumbent;
|
|
}
|
|
|
|
//
|
|
// Make the passed-in variable information become a member of the
|
|
// global uniform block. If this doesn't exist yet, make it.
|
|
//
|
|
void TParseContextBase::growGlobalUniformBlock(TSourceLoc& loc, TType& memberType, TString& memberName)
|
|
{
|
|
// make the global block, if not yet made
|
|
if (globalUniformBlock == nullptr) {
|
|
TString& blockName = *NewPoolTString(getGlobalUniformBlockName());
|
|
TQualifier blockQualifier;
|
|
blockQualifier.clear();
|
|
blockQualifier.storage = EvqUniform;
|
|
TType blockType(new TTypeList, blockName, blockQualifier);
|
|
TString* instanceName = NewPoolTString("");
|
|
globalUniformBlock = new TVariable(instanceName, blockType, true);
|
|
firstNewMember = 0;
|
|
}
|
|
|
|
// add the requested member as a member to the block
|
|
TType* type = new TType;
|
|
type->shallowCopy(memberType);
|
|
type->setFieldName(memberName);
|
|
TTypeLoc typeLoc = {type, loc};
|
|
globalUniformBlock->getType().getWritableStruct()->push_back(typeLoc);
|
|
}
|
|
|
|
//
|
|
// Insert into the symbol table the global uniform block created in
|
|
// growGlobalUniformBlock(). The variables added as members won't be
|
|
// found unless this is done.
|
|
//
|
|
bool TParseContextBase::insertGlobalUniformBlock()
|
|
{
|
|
if (globalUniformBlock == nullptr)
|
|
return true;
|
|
|
|
int numMembers = (int)globalUniformBlock->getType().getStruct()->size();
|
|
bool inserted = false;
|
|
if (firstNewMember == 0) {
|
|
// This is the first request; we need a normal symbol table insert
|
|
inserted = symbolTable.insert(*globalUniformBlock);
|
|
if (inserted)
|
|
intermediate.addSymbolLinkageNode(linkage, *globalUniformBlock);
|
|
} else if (firstNewMember <= numMembers) {
|
|
// This is a follow-on request; we need to amend the first insert
|
|
inserted = symbolTable.amend(*globalUniformBlock, firstNewMember);
|
|
}
|
|
|
|
if (inserted) {
|
|
finalizeGlobalUniformBlockLayout(*globalUniformBlock);
|
|
firstNewMember = numMembers;
|
|
}
|
|
|
|
return inserted;
|
|
}
|
|
|
|
} // end namespace glslang
|