| // |
| //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 |