// // Copyright (C) 2002-2005 3Dlabs Inc. Ltd. // Copyright (C) 2012-2015 LunarG, Inc. // Copyright (C) 2015-2018 Google, Inc. // Copyright (C) 2017, 2019 ARM Limited. // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. // // 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. // #include "ParseHelper.h" #include "Initialize.h" #include "Scan.h" #include "../OSDependent/osinclude.h" #include <algorithm> #include "preprocessor/PpContext.h" extern int yyparse(glslang::TParseContext*); namespace glslang { TParseContext::TParseContext(TSymbolTable& symbolTable, TIntermediate& interm, bool parsingBuiltins, int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TInfoSink& infoSink, bool forwardCompatible, EShMessages messages, const TString* entryPoint) : … { … } TParseContext::~TParseContext() { … } // Set up all default precisions as needed by the current environment. // Intended just as a TParseContext constructor helper. void TParseContext::setPrecisionDefaults() { … } void TParseContext::setLimits(const TBuiltInResource& r) { … } // // Parse an array of strings using yyparse, going through the // preprocessor to tokenize the shader strings, then through // the GLSL scanner. // // Returns true for successful acceptance of the shader, false if any errors. // bool TParseContext::parseShaderStrings(TPpContext& ppContext, TInputScanner& input, bool versionWillBeError) { … } // This is called from bison when it has a parse (syntax) error // Note though that to stop cascading errors, we set EOF, which // will usually cause a syntax error, so be more accurate that // compilation is terminating. void TParseContext::parserError(const char* s) { … } void TParseContext::growGlobalUniformBlock(const TSourceLoc& loc, TType& memberType, const TString& memberName, TTypeList* typeList) { … } void TParseContext::growAtomicCounterBlock(int binding, const TSourceLoc& loc, TType& memberType, const TString& memberName, TTypeList* typeList) { … } const char* TParseContext::getGlobalUniformBlockName() const { … } void TParseContext::finalizeGlobalUniformBlockLayout(TVariable&) { … } void TParseContext::setUniformBlockDefaults(TType& block) const { … } const char* TParseContext::getAtomicCounterBlockName() const { … } void TParseContext::finalizeAtomicCounterBlockLayout(TVariable&) { … } void TParseContext::setAtomicCounterBlockDefaults(TType& block) const { … } void TParseContext::setInvariant(const TSourceLoc& loc, const char* builtin) { … } void TParseContext::handlePragma(const TSourceLoc& loc, const TVector<TString>& tokens) { … } // // Handle seeing a variable identifier in the grammar. // TIntermTyped* TParseContext::handleVariable(const TSourceLoc& loc, TSymbol* symbol, const TString* string) { … } // // Handle seeing a base[index] dereference in the grammar. // TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index) { … } // for ES 2.0 (version 100) limitations for almost all index operations except vertex-shader uniforms void TParseContext::handleIndexLimits(const TSourceLoc& /*loc*/, TIntermTyped* base, TIntermTyped* index) { … } // 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 TParseContext::makeEditable(TSymbol*& symbol) { … } // Return true if this is a geometry shader input array or tessellation control output array // or mesh shader output array. bool TParseContext::isIoResizeArray(const TType& type) const { … } // If an array is not isIoResizeArray() but is an io array, make sure it has the right size void TParseContext::fixIoArraySize(const TSourceLoc& loc, TType& type) { … } // Issue any errors if the non-array object is missing arrayness WRT // shader I/O that has array requirements. // All arrayness checking is handled in array paths, this is for void TParseContext::ioArrayCheck(const TSourceLoc& loc, const TType& type, const TString& identifier) { … } // Handle a dereference of a geometry shader input array or tessellation control output array. // See ioArraySymbolResizeList comment in ParseHelper.h. // void TParseContext::handleIoResizeArrayAccess(const TSourceLoc& /*loc*/, TIntermTyped* base) { … } // If there has been an input primitive declaration (geometry shader) or an output // number of vertices declaration(tessellation shader), make sure all input array types // match it in size. Types come either from nodes in the AST or symbols in the // symbol table. // // Types without an array size will be given one. // Types already having a size that is wrong will get an error. // void TParseContext::checkIoArraysConsistency(const TSourceLoc &loc, bool tailOnly) { … } int TParseContext::getIoArrayImplicitSize(const TQualifier &qualifier, TString *featureString) const { … } void TParseContext::checkIoArrayConsistency(const TSourceLoc& loc, int requiredSize, const char* feature, TType& type, const TString& name) { … } // Handle seeing a binary node with a math operation. // Returns nullptr if not semantically allowed. TIntermTyped* TParseContext::handleBinaryMath(const TSourceLoc& loc, const char* str, TOperator op, TIntermTyped* left, TIntermTyped* right) { … } // Handle seeing a unary node with a math operation. TIntermTyped* TParseContext::handleUnaryMath(const TSourceLoc& loc, const char* str, TOperator op, TIntermTyped* childNode) { … } // // Handle seeing a base.field dereference in the grammar. // TIntermTyped* TParseContext::handleDotDereference(const TSourceLoc& loc, TIntermTyped* base, const TString& field) { … } // // Handle seeing a base.swizzle, a subset of base.identifier in the grammar. // TIntermTyped* TParseContext::handleDotSwizzle(const TSourceLoc& loc, TIntermTyped* base, const TString& field) { … } void TParseContext::blockMemberExtensionCheck(const TSourceLoc& loc, const TIntermTyped* base, int member, const TString& memberName) { … } // // Handle seeing a function declarator in the grammar. This is the precursor // to recognizing a function prototype or function definition. // TFunction* TParseContext::handleFunctionDeclarator(const TSourceLoc& loc, TFunction& function, bool prototype) { … } // // Handle seeing the function prototype in front of a function definition in the grammar. // The body is handled after this function returns. // TIntermAggregate* TParseContext::handleFunctionDefinition(const TSourceLoc& loc, TFunction& function) { … } // // Handle seeing function call syntax in the grammar, which could be any of // - .length() method // - constructor // - a call to a built-in function mapped to an operator // - a call to a built-in function that will remain a function call (e.g., texturing) // - user function // - subroutine call (not implemented yet) // TIntermTyped* TParseContext::handleFunctionCall(const TSourceLoc& loc, TFunction* function, TIntermNode* arguments) { … } TIntermTyped* TParseContext::handleBuiltInFunctionCall(TSourceLoc loc, TIntermNode* arguments, const TFunction& function) { … } // "The operation of a built-in function can have a different precision // qualification than the precision qualification of the resulting value. // These two precision qualifications are established as follows. // // The precision qualification of the operation of a built-in function is // based on the precision qualification of its input arguments and formal // parameters: When a formal parameter specifies a precision qualifier, // that is used, otherwise, the precision qualification of the calling // argument is used. The highest precision of these will be the precision // qualification of the operation of the built-in function. Generally, // this is applied across all arguments to a built-in function, with the // exceptions being: // - bitfieldExtract and bitfieldInsert ignore the 'offset' and 'bits' // arguments. // - interpolateAt* functions only look at the 'interpolant' argument. // // The precision qualification of the result of a built-in function is // determined in one of the following ways: // // - For the texture sampling, image load, and image store functions, // the precision of the return type matches the precision of the // sampler type // // Otherwise: // // - For prototypes that do not specify a resulting precision qualifier, // the precision will be the same as the precision of the operation. // // - For prototypes that do specify a resulting precision qualifier, // the specified precision qualifier is the precision qualification of // the result." // void TParseContext::computeBuiltinPrecisions(TIntermTyped& node, const TFunction& function) { … } TIntermNode* TParseContext::handleReturnValue(const TSourceLoc& loc, TIntermTyped* value) { … } // See if the operation is being done in an illegal location. void TParseContext::checkLocation(const TSourceLoc& loc, TOperator op) { … } // Finish processing object.length(). This started earlier in handleDotDereference(), where // the ".length" part was recognized and semantically checked, and finished here where the // function syntax "()" is recognized. // // Return resulting tree node. TIntermTyped* TParseContext::handleLengthMethod(const TSourceLoc& loc, TFunction* function, TIntermNode* intermNode) { … } // // Add any needed implicit conversions for function-call arguments to input parameters. // void TParseContext::addInputArgumentConversions(const TFunction& function, TIntermNode*& arguments) const { … } // // Add any needed implicit output conversions for function-call arguments. This // can require a new tree topology, complicated further by whether the function // has a return value. // // Returns a node of a subtree that evaluates to the return value of the function. // TIntermTyped* TParseContext::addOutputArgumentConversions(const TFunction& function, TIntermAggregate& intermNode) const { … } TIntermTyped* TParseContext::addAssign(const TSourceLoc& loc, TOperator op, TIntermTyped* left, TIntermTyped* right) { … } void TParseContext::memorySemanticsCheck(const TSourceLoc& loc, const TFunction& fnCandidate, const TIntermOperator& callNode) { … } // // Do additional checking of built-in function calls that is not caught // by normal semantic checks on argument type, extension tagging, etc. // // Assumes there has been a semantically correct match to a built-in function prototype. // void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCandidate, TIntermOperator& callNode) { … } // Deprecated! Use PureOperatorBuiltins == true instead, in which case this // functionality is handled in builtInOpCheck() instead of here. // // Do additional checking of built-in function calls that were not mapped // to built-in operations (e.g., texturing functions). // // Assumes there has been a semantically correct match to a built-in function. // void TParseContext::nonOpBuiltInCheck(const TSourceLoc& loc, const TFunction& fnCandidate, TIntermAggregate& callNode) { … } // // Do any extra checking for a user function call. // void TParseContext::userFunctionCallCheck(const TSourceLoc& loc, TIntermAggregate& callNode) { … } // // Emit an error if this is a sampler constructor // void TParseContext::samplerConstructorLocationCheck(const TSourceLoc& loc, const char* token, TIntermNode* node) { … } // // Handle seeing a built-in constructor in a grammar production. // TFunction* TParseContext::handleConstructorCall(const TSourceLoc& loc, const TPublicType& publicType) { … } // Handle seeing a precision qualifier in the grammar. void TParseContext::handlePrecisionQualifier(const TSourceLoc& /*loc*/, TQualifier& qualifier, TPrecisionQualifier precision) { … } // Check for messages to give on seeing a precision qualifier used in a // declaration in the grammar. void TParseContext::checkPrecisionQualifier(const TSourceLoc& loc, TPrecisionQualifier) { … } // // Same error message for all places assignments don't work. // void TParseContext::assignError(const TSourceLoc& loc, const char* op, TString left, TString right) { … } // // Same error message for all places unary operations don't work. // void TParseContext::unaryOpError(const TSourceLoc& loc, const char* op, TString operand) { … } // // Same error message for all binary operations don't work. // void TParseContext::binaryOpError(const TSourceLoc& loc, const char* op, TString left, TString right) { … } // // A basic type of EbtVoid is a key that the name string was seen in the source, but // it was not found as a variable in the symbol table. If so, give the error // message and insert a dummy variable in the symbol table to prevent future errors. // void TParseContext::variableCheck(TIntermTyped*& nodePtr) { … } // // 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 TParseContext::lValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node) { … } // Test for and give an error if the node can't be read from. void TParseContext::rValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node) { … } // // Both test, and if necessary spit out an error, to see if the node is really // a constant. // void TParseContext::constantValueCheck(TIntermTyped* node, const char* token) { … } // // Both test, and if necessary spit out an error, to see if the node is really // a 32-bit integer or can implicitly convert to one. // void TParseContext::integerCheck(const TIntermTyped* node, const char* token) { … } // // Both test, and if necessary spit out an error, to see if we are currently // globally scoped. // void TParseContext::globalCheck(const TSourceLoc& loc, const char* token) { … } // // Reserved errors for GLSL. // void TParseContext::reservedErrorCheck(const TSourceLoc& loc, const TString& identifier) { … } // // Reserved errors for the preprocessor. // void TParseContext::reservedPpErrorCheck(const TSourceLoc& loc, const char* identifier, const char* op) { … } // // See if this version/profile allows use of the line-continuation character '\'. // // Returns true if a line continuation should be done. // bool TParseContext::lineContinuationCheck(const TSourceLoc& loc, bool endOfComment) { … } bool TParseContext::builtInName(const TString& identifier) { … } // // Make sure there is enough data and not too many arguments provided to the // constructor to build something of the type of the constructor. Also returns // the type of the constructor. // // Part of establishing type is establishing specialization-constness. // We don't yet know "top down" whether type is a specialization constant, // but a const constructor can becomes a specialization constant if any of // its children are, subject to KHR_vulkan_glsl rules: // // - int(), uint(), and bool() constructors for type conversions // from any of the following types to any of the following types: // * int // * uint // * bool // - vector versions of the above conversion constructors // // Returns true if there was an error in construction. // bool TParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, TFunction& function, TOperator op, TType& type) { … } // Verify all the correct semantics for constructing a combined texture/sampler. // Return true if the semantics are incorrect. bool TParseContext::constructorTextureSamplerError(const TSourceLoc& loc, const TFunction& function) { … } // Checks to see if a void variable has been declared and raise an error message for such a case // // returns true in case of an error // bool TParseContext::voidErrorCheck(const TSourceLoc& loc, const TString& identifier, const TBasicType basicType) { … } // Checks to see if the node (for the expression) contains a scalar boolean expression or not void TParseContext::boolCheck(const TSourceLoc& loc, const TIntermTyped* type) { … } // This function checks to see if the node (for the expression) contains a scalar boolean expression or not void TParseContext::boolCheck(const TSourceLoc& loc, const TPublicType& pType) { … } void TParseContext::samplerCheck(const TSourceLoc& loc, const TType& type, const TString& identifier, TIntermTyped* /*initializer*/) { … } void TParseContext::atomicUintCheck(const TSourceLoc& loc, const TType& type, const TString& identifier) { … } void TParseContext::accStructCheck(const TSourceLoc& loc, const TType& type, const TString& identifier) { … } void TParseContext::transparentOpaqueCheck(const TSourceLoc& loc, const TType& type, const TString& identifier) { … } // // Qualifier checks knowing the qualifier and that it is a member of a struct/block. // void TParseContext::memberQualifierCheck(glslang::TPublicType& publicType) { … } // // Check/fix just a full qualifier (no variables or types yet, but qualifier is complete) at global level. // void TParseContext::globalQualifierFixCheck(const TSourceLoc& loc, TQualifier& qualifier, bool isMemberCheck, const TPublicType* publicType) { … } // // Check a full qualifier and type (no variable yet) at global level. // void TParseContext::globalQualifierTypeCheck(const TSourceLoc& loc, const TQualifier& qualifier, const TPublicType& publicType) { … } // // Merge characteristics of the 'src' qualifier into the 'dst'. // If there is duplication, issue error messages, unless 'force' // is specified, which means to just override default settings. // // Also, when force is false, it will be assumed that 'src' follows // 'dst', for the purpose of error checking order for versions // that require specific orderings of qualifiers. // void TParseContext::mergeQualifiers(const TSourceLoc& loc, TQualifier& dst, const TQualifier& src, bool force) { … } void TParseContext::setDefaultPrecision(const TSourceLoc& loc, TPublicType& publicType, TPrecisionQualifier qualifier) { … } // used to flatten the sampler type space into a single dimension // correlates with the declaration of defaultSamplerPrecision[] int TParseContext::computeSamplerTypeIndex(TSampler& sampler) { … } TPrecisionQualifier TParseContext::getDefaultPrecision(TPublicType& publicType) { … } void TParseContext::precisionQualifierCheck(const TSourceLoc& loc, TBasicType baseType, TQualifier& qualifier, bool isCoopMat) { … } void TParseContext::parameterTypeCheck(const TSourceLoc& loc, TStorageQualifier qualifier, const TType& type) { … } bool TParseContext::containsFieldWithBasicType(const TType& type, TBasicType basicType) { … } // // Do size checking for an array type's size. // void TParseContext::arraySizeCheck(const TSourceLoc& loc, TIntermTyped* expr, TArraySize& sizePair, const char* sizeType, const bool allowZero) { … } // // See if this qualifier can be an array. // // Returns true if there is an error. // bool TParseContext::arrayQualifierError(const TSourceLoc& loc, const TQualifier& qualifier) { … } // // See if this qualifier and type combination can be an array. // Assumes arrayQualifierError() was also called to catch the type-invariant tests. // // Returns true if there is an error. // bool TParseContext::arrayError(const TSourceLoc& loc, const TType& type) { … } // // Require array to be completely sized // void TParseContext::arraySizeRequiredCheck(const TSourceLoc& loc, const TArraySizes& arraySizes) { … } void TParseContext::structArrayCheck(const TSourceLoc& /*loc*/, const TType& type) { … } void TParseContext::arraySizesCheck(const TSourceLoc& loc, const TQualifier& qualifier, TArraySizes* arraySizes, const TIntermTyped* initializer, bool lastMember) { … } void TParseContext::arrayOfArrayVersionCheck(const TSourceLoc& loc, const TArraySizes* sizes) { … } // // Do all the semantic checking for declaring or redeclaring an array, with and // without a size, and make the right changes to the symbol table. // void TParseContext::declareArray(const TSourceLoc& loc, const TString& identifier, const TType& type, TSymbol*& symbol) { … } // Policy and error check for needing a runtime sized array. void TParseContext::checkRuntimeSizable(const TSourceLoc& loc, const TIntermTyped& base) { … } // Policy decision for whether a run-time .length() is allowed. bool TParseContext::isRuntimeLength(const TIntermTyped& base) const { … } // Check if mesh perviewNV attributes have a view dimension // and resize it to gl_MaxMeshViewCountNV when implicitly sized. void TParseContext::checkAndResizeMeshViewDim(const TSourceLoc& loc, TType& type, bool isBlockMember) { … } // Returns true if the first argument to the #line directive is the line number for the next line. // // Desktop, pre-version 3.30: "After processing this directive // (including its new-line), the implementation will behave as if it is compiling at line number line+1 and // source string number source-string-number." // // Desktop, version 3.30 and later, and ES: "After processing this directive // (including its new-line), the implementation will behave as if it is compiling at line number line and // source string number source-string-number. bool TParseContext::lineDirectiveShouldSetNextLine() const { … } // // Enforce non-initializer type/qualifier rules. // void TParseContext::nonInitConstCheck(const TSourceLoc& loc, TString& identifier, TType& type) { … } // // See if the identifier is a built-in symbol that can be redeclared, and if so, // copy the symbol table's read-only built-in variable to the current // global level, where it can be modified based on the passed in type. // // Returns nullptr if no redeclaration took place; meaning a normal declaration still // needs to occur for it, not necessarily an error. // // Returns a redeclared and type-modified variable if a redeclarated occurred. // TSymbol* TParseContext::redeclareBuiltinVariable(const TSourceLoc& loc, const TString& identifier, const TQualifier& qualifier, const TShaderQualifiers& publicType) { … } // // Either redeclare the requested block, or give an error message why it can't be done. // // TODO: functionality: explicitly sizing members of redeclared blocks is not giving them an explicit size void TParseContext::redeclareBuiltinBlock(const TSourceLoc& loc, TTypeList& newTypeList, const TString& blockName, const TString* instanceName, TArraySizes* arraySizes) { … } void TParseContext::paramCheckFixStorage(const TSourceLoc& loc, const TStorageQualifier& qualifier, TType& type) { … } void TParseContext::paramCheckFix(const TSourceLoc& loc, const TQualifier& qualifier, TType& type) { … } void TParseContext::nestedBlockCheck(const TSourceLoc& loc) { … } void TParseContext::nestedStructCheck(const TSourceLoc& loc) { … } void TParseContext::arrayObjectCheck(const TSourceLoc& loc, const TType& type, const char* op) { … } void TParseContext::opaqueCheck(const TSourceLoc& loc, const TType& type, const char* op) { … } void TParseContext::referenceCheck(const TSourceLoc& loc, const TType& type, const char* op) { … } void TParseContext::storage16BitAssignmentCheck(const TSourceLoc& loc, const TType& type, const char* op) { … } void TParseContext::specializationCheck(const TSourceLoc& loc, const TType& type, const char* op) { … } void TParseContext::structTypeCheck(const TSourceLoc& /*loc*/, TPublicType& publicType) { … } // // See if this loop satisfies the limitations for ES 2.0 (version 100) for loops in Appendex A: // // "The loop index has type int or float. // // "The for statement has the form: // for ( init-declaration ; condition ; expression ) // init-declaration has the form: type-specifier identifier = constant-expression // condition has the form: loop-index relational_operator constant-expression // where relational_operator is one of: > >= < <= == or != // expression [sic] has one of the following forms: // loop-index++ // loop-index-- // loop-index += constant-expression // loop-index -= constant-expression // // The body is handled in an AST traversal. // void TParseContext::inductiveLoopCheck(const TSourceLoc& loc, TIntermNode* init, TIntermLoop* loop) { … } // Do limit checks for built-in arrays. void TParseContext::arrayLimitCheck(const TSourceLoc& loc, const TString& identifier, int size) { … } // See if the provided value is less than or equal to the symbol indicated by limit, // which should be a constant in the symbol table. void TParseContext::limitCheck(const TSourceLoc& loc, int value, const char* limit, const char* feature) { … } // // Do any additional error checking, etc., once we know the parsing is done. // void TParseContext::finish() { … } // // Layout qualifier stuff. // // Put the id's layout qualification into the public type, for qualifiers not having a number set. // This is before we know any type information for error checking. void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publicType, TString& id) { … } // Put the id's layout qualifier value into the public type, for qualifiers having a number set. // This is before we know any type information for error checking. void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publicType, TString& id, const TIntermTyped* node) { … } // Merge any layout qualifier information from src into dst, leaving everything else in dst alone // // "More than one layout qualifier may appear in a single declaration. // Additionally, the same layout-qualifier-name can occur multiple times // within a layout qualifier or across multiple layout qualifiers in the // same declaration. When the same layout-qualifier-name occurs // multiple times, in a single declaration, the last occurrence overrides // the former occurrence(s). Further, if such a layout-qualifier-name // will effect subsequent declarations or other observable behavior, it // is only the last occurrence that will have any effect, behaving as if // the earlier occurrence(s) within the declaration are not present. // This is also true for overriding layout-qualifier-names, where one // overrides the other (e.g., row_major vs. column_major); only the last // occurrence has any effect." void TParseContext::mergeObjectLayoutQualifiers(TQualifier& dst, const TQualifier& src, bool inheritOnly) { … } // Do error layout error checking given a full variable/block declaration. void TParseContext::layoutObjectCheck(const TSourceLoc& loc, const TSymbol& symbol) { … } // "For some blocks declared as arrays, the location can only be applied at the block level: // When a block is declared as an array where additional locations are needed for each member // for each block array element, it is a compile-time error to specify locations on the block // members. That is, when locations would be under specified by applying them on block members, // they are not allowed on block members. For arrayed interfaces (those generally having an // extra level of arrayness due to interface expansion), the outer array is stripped before // applying this rule." void TParseContext::layoutMemberLocationArrayCheck(const TSourceLoc& loc, bool memberWithLocation, TArraySizes* arraySizes) { … } // Do layout error checking with respect to a type. void TParseContext::layoutTypeCheck(const TSourceLoc& loc, const TType& type) { … } static bool storageCanHaveLayoutInBlock(const enum TStorageQualifier storage) { … } // Do layout error checking that can be done within a layout qualifier proper, not needing to know // if there are blocks, atomic counters, variables, etc. void TParseContext::layoutQualifierCheck(const TSourceLoc& loc, const TQualifier& qualifier) { … } // For places that can't have shader-level layout qualifiers void TParseContext::checkNoShaderLayouts(const TSourceLoc& loc, const TShaderQualifiers& shaderQualifiers) { … } // Correct and/or advance an object's offset layout qualifier. void TParseContext::fixOffset(const TSourceLoc& loc, TSymbol& symbol) { … } // // Look up a function name in the symbol table, and make sure it is a function. // // Return the function symbol if found, otherwise nullptr. // const TFunction* TParseContext::findFunction(const TSourceLoc& loc, const TFunction& call, bool& builtIn) { … } // Function finding algorithm for ES and desktop 110. const TFunction* TParseContext::findFunctionExact(const TSourceLoc& loc, const TFunction& call, bool& builtIn) { … } // Function finding algorithm for desktop versions 120 through 330. const TFunction* TParseContext::findFunction120(const TSourceLoc& loc, const TFunction& call, bool& builtIn) { … } // Function finding algorithm for desktop version 400 and above. // // "When function calls are resolved, an exact type match for all the arguments // is sought. If an exact match is found, all other functions are ignored, and // the exact match is used. If no exact match is found, then the implicit // conversions in section 4.1.10 Implicit Conversions will be applied to find // a match. Mismatched types on input parameters (in or inout or default) must // have a conversion from the calling argument type to the formal parameter type. // Mismatched types on output parameters (out or inout) must have a conversion // from the formal parameter type to the calling argument type. // // "If implicit conversions can be used to find more than one matching function, // a single best-matching function is sought. To determine a best match, the // conversions between calling argument and formal parameter types are compared // for each function argument and pair of matching functions. After these // comparisons are performed, each pair of matching functions are compared. // A function declaration A is considered a better match than function // declaration B if // // * for at least one function argument, the conversion for that argument in A // is better than the corresponding conversion in B; and // * there is no function argument for which the conversion in B is better than // the corresponding conversion in A. // // "If a single function declaration is considered a better match than every // other matching function declaration, it will be used. Otherwise, a // compile-time semantic error for an ambiguous overloaded function call occurs. // // "To determine whether the conversion for a single argument in one match is // better than that for another match, the following rules are applied, in order: // // 1. An exact match is better than a match involving any implicit conversion. // 2. A match involving an implicit conversion from float to double is better // than a match involving any other implicit conversion. // 3. A match involving an implicit conversion from either int or uint to float // is better than a match involving an implicit conversion from either int // or uint to double. // // "If none of the rules above apply to a particular pair of conversions, neither // conversion is considered better than the other." // const TFunction* TParseContext::findFunction400(const TSourceLoc& loc, const TFunction& call, bool& builtIn) { … } // "To determine whether the conversion for a single argument in one match // is better than that for another match, the conversion is assigned of the // three ranks ordered from best to worst: // 1. Exact match: no conversion. // 2. Promotion: integral or floating-point promotion. // 3. Conversion: integral conversion, floating-point conversion, // floating-integral conversion. // A conversion C1 is better than a conversion C2 if the rank of C1 is // better than the rank of C2." const TFunction* TParseContext::findFunctionExplicitTypes(const TSourceLoc& loc, const TFunction& call, bool& builtIn) { … } // // Adjust function calls that aren't declared in Vulkan to a // calls with equivalent effects // TIntermTyped* TParseContext::vkRelaxedRemapFunctionCall(const TSourceLoc& loc, TFunction* function, TIntermNode* arguments) { … } // When a declaration includes a type, but not a variable name, it can be used // to establish defaults. void TParseContext::declareTypeDefaults(const TSourceLoc& loc, const TPublicType& publicType) { … } void TParseContext::coopMatTypeParametersCheck(const TSourceLoc& loc, const TPublicType& publicType) { … } bool TParseContext::vkRelaxedRemapUniformVariable(const TSourceLoc& loc, TString& identifier, const TPublicType& publicType, TArraySizes*, TIntermTyped* initializer, TType& type) { … } template <typename Function> static void ForEachOpaque(const TType& type, const TString& path, Function callback) { … } void TParseContext::vkRelaxedRemapUniformMembers(const TSourceLoc& loc, const TPublicType& publicType, const TType& type, const TString& identifier) { … } void TParseContext::vkRelaxedRemapFunctionParameter(TFunction* function, TParameter& param, std::vector<int>* newParams) { … } // // Generates a valid GLSL dereferencing string for the input TIntermNode // struct AccessChainTraverser : public TIntermTraverser { … }; TIntermNode* TParseContext::vkRelaxedRemapFunctionArgument(const TSourceLoc& loc, TFunction* function, TIntermTyped* intermTyped) { … } TIntermTyped* TParseContext::vkRelaxedRemapDotDereference(const TSourceLoc&, TIntermTyped& base, const TType& member, const TString& identifier) { … } // // Do everything necessary to handle a variable (non-block) declaration. // Either redeclaring a variable, or making a new one, updating the symbol // table, and all error checking. // // Returns a subtree node that computes an initializer, if needed. // Returns nullptr if there is no code to execute for initialization. // // 'publicType' is the type part of the declaration (to the left) // 'arraySizes' is the arrayness tagged on the identifier (to the right) // TIntermNode* TParseContext::declareVariable(const TSourceLoc& loc, TString& identifier, const TPublicType& publicType, TArraySizes* arraySizes, TIntermTyped* initializer) { … } // Pick up global defaults from the provide global defaults into dst. void TParseContext::inheritGlobalDefaults(TQualifier& dst) const { … } // // Make an internal-only variable whose name is for debug purposes only // and won't be searched for. Callers will only use the return value to use // the variable, not the name to look it up. It is okay if the name // is the same as other names; there won't be any conflict. // TVariable* TParseContext::makeInternalVariable(const char* name, const TType& type) const { … } // // Declare a non-array variable, the main point being there is no redeclaration // for resizing allowed. // // Return the successfully declared variable. // TVariable* TParseContext::declareNonArray(const TSourceLoc& loc, const TString& identifier, const TType& type) { … } // // Handle all types of initializers from the grammar. // // Returning nullptr just means there is no code to execute to handle the // initializer, which will, for example, be the case for constant initializers. // TIntermNode* TParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyped* initializer, TVariable* variable) { … } // // Reprocess any initializer-list (the "{ ... }" syntax) parts of the // initializer. // // Need to hierarchically assign correct types and implicit // conversions. Will do this mimicking the same process used for // creating a constructor-style initializer, ensuring we get the // same form. However, it has to in parallel walk the 'type' // passed in, as type cannot be deduced from an initializer list. // TIntermTyped* TParseContext::convertInitializerList(const TSourceLoc& loc, const TType& type, TIntermTyped* initializer) { … } // // Test for the correctness of the parameters passed to various constructor functions // and also convert them to the right data type, if allowed and required. // // 'node' is what to construct from. // 'type' is what type to construct. // // Returns nullptr for an error or the constructed node (aggregate or typed) for no error. // TIntermTyped* TParseContext::addConstructor(const TSourceLoc& loc, TIntermNode* node, const TType& type) { … } // Function for constructor implementation. Calls addUnaryMath with appropriate EOp value // for the parameter to the constructor (passed to this function). Essentially, it converts // the parameter types correctly. If a constructor expects an int (like ivec2) and is passed a // float, then float is converted to int. // // Returns nullptr for an error or the constructed node. // TIntermTyped* TParseContext::constructBuiltIn(const TType& type, TOperator op, TIntermTyped* node, const TSourceLoc& loc, bool subset) { … } // This function tests for the type of the parameters to the structure or array constructor. Raises // an error message if the expected type does not match the parameter passed to the constructor. // // Returns nullptr for an error or the input node itself if the expected and the given parameter types match. // TIntermTyped* TParseContext::constructAggregate(TIntermNode* node, const TType& type, int paramCount, const TSourceLoc& loc) { … } // If a memory qualifier is present in 'to', also make it present in 'from'. void TParseContext::inheritMemoryQualifiers(const TQualifier& from, TQualifier& to) { … } // // Update qualifier layoutBindlessImage & layoutBindlessSampler on block member // void TParseContext::updateBindlessQualifier(TType& memberType) { … } // // Do everything needed to add an interface block. // void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, const TString* instanceName, TArraySizes* arraySizes) { … } // // allow storage type of block to be remapped at compile time // void TParseContext::blockStorageRemap(const TSourceLoc&, const TString* instanceName, TQualifier& qualifier) { … } // Do all block-declaration checking regarding the combination of in/out/uniform/buffer // with a particular stage. void TParseContext::blockStageIoCheck(const TSourceLoc& loc, const TQualifier& qualifier) { … } // Do all block-declaration checking regarding its qualifiers. void TParseContext::blockQualifierCheck(const TSourceLoc& loc, const TQualifier& qualifier, bool /*instanceName*/) { … } // // "For a block, this process applies to the entire block, or until the first member // is reached that has a location layout qualifier. When a block member is declared with a location // qualifier, its location comes from that qualifier: The member's location qualifier overrides the block-level // declaration. Subsequent members are again assigned consecutive locations, based on the newest location, // until the next member declared with a location qualifier. The values used for locations do not have to be // declared in increasing order." void TParseContext::fixBlockLocations(const TSourceLoc& loc, TQualifier& qualifier, TTypeList& typeList, bool memberWithLocation, bool memberWithoutLocation) { … } void TParseContext::fixXfbOffsets(TQualifier& qualifier, TTypeList& typeList) { … } // Calculate and save the offset of each block member, using the recursively // defined block offset rules and the user-provided offset and align. // // Also, compute and save the total size of the block. For the block's size, arrayness // is not taken into account, as each element is backed by a separate buffer. // void TParseContext::fixBlockUniformOffsets(TQualifier& qualifier, TTypeList& typeList) { … } // // Spread LayoutMatrix to uniform block member, if a uniform block member is a struct, // we need spread LayoutMatrix to this struct member too. and keep this rule for recursive. // void TParseContext::fixBlockUniformLayoutMatrix(TQualifier& qualifier, TTypeList* originTypeList, TTypeList* tmpTypeList) { … } // // Spread LayoutPacking to matrix or aggregate block members. If a block member is a struct or // array of struct, spread LayoutPacking recursively to its matrix or aggregate members. // void TParseContext::fixBlockUniformLayoutPacking(TQualifier& qualifier, TTypeList* originTypeList, TTypeList* tmpTypeList) { … } // For an identifier that is already declared, add more qualification to it. void TParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, const TString& identifier) { … } void TParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, TIdentifierList& identifiers) { … } // Make sure 'invariant' isn't being applied to a non-allowed object. void TParseContext::invariantCheck(const TSourceLoc& loc, const TQualifier& qualifier) { … } // // Updating default qualifier for the case of a declaration with just a qualifier, // no type, block, or identifier. // void TParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, const TPublicType& publicType) { … } // // Take the sequence of statements that has been built up since the last case/default, // put it on the list of top-level nodes for the current (inner-most) switch statement, // and follow that by the case/default we are on now. (See switch topology comment on // TIntermSwitch.) // void TParseContext::wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode) { … } // // Turn the top-level node sequence built up of wrapupSwitchSubsequence9) // into a switch node. // TIntermNode* TParseContext::addSwitch(const TSourceLoc& loc, TIntermTyped* expression, TIntermAggregate* lastStatements) { … } // // When a struct used in block, and has it's own layout packing, layout matrix, // record the origin structure of a struct to map, and Record the structure copy to the copy table, // const TTypeList* TParseContext::recordStructCopy(TStructRecord& record, const TType* originType, const TType* tmpType) { … } TLayoutFormat TParseContext::mapLegacyLayoutFormat(TLayoutFormat legacyLayoutFormat, TBasicType imageType) { … } } // end namespace glslang