From 54ee28f4d031499babb95104924dfe1bd8af52ee Mon Sep 17 00:00:00 2001 From: John Kessenich Date: Sat, 11 Mar 2017 14:13:00 -0700 Subject: [PATCH] HLSL: Add scoping operator, accept static member functions, and support calling them. --- .../hlsl.staticMemberFunction.frag.out | 199 ++++++++++++++++++ Test/hlsl.staticMemberFunction.frag | 22 ++ glslang/Include/intermediate.h | 1 + gtests/Hlsl.FromFile.cpp | 1 + hlsl/hlslGrammar.cpp | 184 ++++++++++++---- hlsl/hlslGrammar.h | 11 +- hlsl/hlslOpMap.cpp | 2 + hlsl/hlslParseHelper.cpp | 46 ++++ hlsl/hlslParseHelper.h | 5 + hlsl/hlslScanContext.cpp | 2 + hlsl/hlslTokens.h | 1 - 11 files changed, 424 insertions(+), 50 deletions(-) create mode 100755 Test/baseResults/hlsl.staticMemberFunction.frag.out create mode 100755 Test/hlsl.staticMemberFunction.frag diff --git a/Test/baseResults/hlsl.staticMemberFunction.frag.out b/Test/baseResults/hlsl.staticMemberFunction.frag.out new file mode 100755 index 00000000..0bca0eca --- /dev/null +++ b/Test/baseResults/hlsl.staticMemberFunction.frag.out @@ -0,0 +1,199 @@ +hlsl.staticMemberFunction.frag +Shader version: 450 +gl_FragCoord origin is upper left +0:? Sequence +0:5 Function Definition: Test::staticMemFun(vf4; (global 4-component vector of float) +0:5 Function Parameters: +0:5 'a' (in 4-component vector of float) +0:? Sequence +0:6 Branch: Return with expression +0:6 vector-scale (temp 4-component vector of float) +0:6 Constant: +0:6 2.000000 +0:6 'a' (in 4-component vector of float) +0:9 Function Definition: Test::staticMemFun(i1; (global int) +0:9 Function Parameters: +0:9 'a' (in int) +0:? Sequence +0:10 Branch: Return with expression +0:10 add (temp int) +0:10 Constant: +0:10 2 (const int) +0:10 'a' (in int) +0:16 Function Definition: @main( (temp 4-component vector of float) +0:16 Function Parameters: +0:? Sequence +0:18 Sequence +0:18 move second child to first child (temp 4-component vector of float) +0:18 'f4' (temp 4-component vector of float) +0:? Constant: +0:? 1.000000 +0:? 1.000000 +0:? 1.000000 +0:? 1.000000 +0:19 add second child into first child (temp 4-component vector of float) +0:19 'f4' (temp 4-component vector of float) +0:19 Function Call: Test::staticMemFun(vf4; (global 4-component vector of float) +0:? Constant: +0:? 5.000000 +0:? 5.000000 +0:? 5.000000 +0:? 5.000000 +0:20 add second child into first child (temp 4-component vector of float) +0:20 'f4' (temp 4-component vector of float) +0:20 Convert int to float (temp float) +0:20 Function Call: Test::staticMemFun(i1; (global int) +0:20 Constant: +0:20 7 (const int) +0:21 Branch: Return with expression +0:21 'f4' (temp 4-component vector of float) +0:16 Function Definition: main( (temp void) +0:16 Function Parameters: +0:? Sequence +0:16 move second child to first child (temp 4-component vector of float) +0:? '@entryPointOutput' (layout(location=0 ) out 4-component vector of float) +0:16 Function Call: @main( (temp 4-component vector of float) +0:? Linker Objects +0:? '@entryPointOutput' (layout(location=0 ) out 4-component vector of float) + + +Linked fragment stage: + + +Shader version: 450 +gl_FragCoord origin is upper left +0:? Sequence +0:5 Function Definition: Test::staticMemFun(vf4; (global 4-component vector of float) +0:5 Function Parameters: +0:5 'a' (in 4-component vector of float) +0:? Sequence +0:6 Branch: Return with expression +0:6 vector-scale (temp 4-component vector of float) +0:6 Constant: +0:6 2.000000 +0:6 'a' (in 4-component vector of float) +0:9 Function Definition: Test::staticMemFun(i1; (global int) +0:9 Function Parameters: +0:9 'a' (in int) +0:? Sequence +0:10 Branch: Return with expression +0:10 add (temp int) +0:10 Constant: +0:10 2 (const int) +0:10 'a' (in int) +0:16 Function Definition: @main( (temp 4-component vector of float) +0:16 Function Parameters: +0:? Sequence +0:18 Sequence +0:18 move second child to first child (temp 4-component vector of float) +0:18 'f4' (temp 4-component vector of float) +0:? Constant: +0:? 1.000000 +0:? 1.000000 +0:? 1.000000 +0:? 1.000000 +0:19 add second child into first child (temp 4-component vector of float) +0:19 'f4' (temp 4-component vector of float) +0:19 Function Call: Test::staticMemFun(vf4; (global 4-component vector of float) +0:? Constant: +0:? 5.000000 +0:? 5.000000 +0:? 5.000000 +0:? 5.000000 +0:20 add second child into first child (temp 4-component vector of float) +0:20 'f4' (temp 4-component vector of float) +0:20 Convert int to float (temp float) +0:20 Function Call: Test::staticMemFun(i1; (global int) +0:20 Constant: +0:20 7 (const int) +0:21 Branch: Return with expression +0:21 'f4' (temp 4-component vector of float) +0:16 Function Definition: main( (temp void) +0:16 Function Parameters: +0:? Sequence +0:16 move second child to first child (temp 4-component vector of float) +0:? '@entryPointOutput' (layout(location=0 ) out 4-component vector of float) +0:16 Function Call: @main( (temp 4-component vector of float) +0:? Linker Objects +0:? '@entryPointOutput' (layout(location=0 ) out 4-component vector of float) + +// Module Version 10000 +// Generated by (magic number): 80001 +// Id's are bound by 54 + + Capability Shader + 1: ExtInstImport "GLSL.std.450" + MemoryModel Logical GLSL450 + EntryPoint Fragment 4 "main" 52 + ExecutionMode 4 OriginUpperLeft + Name 4 "main" + Name 11 "Test::staticMemFun(vf4;" + Name 10 "a" + Name 17 "Test::staticMemFun(i1;" + Name 16 "a" + Name 20 "@main(" + Name 32 "f4" + Name 37 "param" + Name 42 "param" + Name 52 "@entryPointOutput" + Decorate 52(@entryPointOutput) Location 0 + 2: TypeVoid + 3: TypeFunction 2 + 6: TypeFloat 32 + 7: TypeVector 6(float) 4 + 8: TypePointer Function 7(fvec4) + 9: TypeFunction 7(fvec4) 8(ptr) + 13: TypeInt 32 1 + 14: TypePointer Function 13(int) + 15: TypeFunction 13(int) 14(ptr) + 19: TypeFunction 7(fvec4) + 22: 6(float) Constant 1073741824 + 27: 13(int) Constant 2 + 33: 6(float) Constant 1065353216 + 34: 7(fvec4) ConstantComposite 33 33 33 33 + 35: 6(float) Constant 1084227584 + 36: 7(fvec4) ConstantComposite 35 35 35 35 + 41: 13(int) Constant 7 + 51: TypePointer Output 7(fvec4) +52(@entryPointOutput): 51(ptr) Variable Output + 4(main): 2 Function None 3 + 5: Label + 53: 7(fvec4) FunctionCall 20(@main() + Store 52(@entryPointOutput) 53 + Return + FunctionEnd +11(Test::staticMemFun(vf4;): 7(fvec4) Function None 9 + 10(a): 8(ptr) FunctionParameter + 12: Label + 23: 7(fvec4) Load 10(a) + 24: 7(fvec4) VectorTimesScalar 23 22 + ReturnValue 24 + FunctionEnd +17(Test::staticMemFun(i1;): 13(int) Function None 15 + 16(a): 14(ptr) FunctionParameter + 18: Label + 28: 13(int) Load 16(a) + 29: 13(int) IAdd 27 28 + ReturnValue 29 + FunctionEnd + 20(@main(): 7(fvec4) Function None 19 + 21: Label + 32(f4): 8(ptr) Variable Function + 37(param): 8(ptr) Variable Function + 42(param): 14(ptr) Variable Function + Store 32(f4) 34 + Store 37(param) 36 + 38: 7(fvec4) FunctionCall 11(Test::staticMemFun(vf4;) 37(param) + 39: 7(fvec4) Load 32(f4) + 40: 7(fvec4) FAdd 39 38 + Store 32(f4) 40 + Store 42(param) 41 + 43: 13(int) FunctionCall 17(Test::staticMemFun(i1;) 42(param) + 44: 6(float) ConvertSToF 43 + 45: 7(fvec4) Load 32(f4) + 46: 7(fvec4) CompositeConstruct 44 44 44 44 + 47: 7(fvec4) FAdd 45 46 + Store 32(f4) 47 + 48: 7(fvec4) Load 32(f4) + ReturnValue 48 + FunctionEnd diff --git a/Test/hlsl.staticMemberFunction.frag b/Test/hlsl.staticMemberFunction.frag new file mode 100755 index 00000000..700aa82c --- /dev/null +++ b/Test/hlsl.staticMemberFunction.frag @@ -0,0 +1,22 @@ +struct Test +{ + float4 memVar; + static float4 staticMemFun(float4 a) : SV_Position + { + return 2 * a; + } + static int staticMemFun(int a) : SV_Position + { + return 2 + a; + } + int i; +}; + +float4 main() : SV_Target0 +{ + Test test; + float4 f4 = float4(1.0,1.0,1.0,1.0); + f4 += Test::staticMemFun(float4(5.0f,5.0f,5.0f,5.0f)); + f4 += Test::staticMemFun(7); + return f4; +} diff --git a/glslang/Include/intermediate.h b/glslang/Include/intermediate.h index 4b58e9b0..934994d9 100644 --- a/glslang/Include/intermediate.h +++ b/glslang/Include/intermediate.h @@ -183,6 +183,7 @@ enum TOperator { EOpVectorSwizzle, EOpMethod, + EOpScoping, // // Built-in functions mapped to operators diff --git a/gtests/Hlsl.FromFile.cpp b/gtests/Hlsl.FromFile.cpp index 3ac6585d..f2b480cb 100644 --- a/gtests/Hlsl.FromFile.cpp +++ b/gtests/Hlsl.FromFile.cpp @@ -212,6 +212,7 @@ INSTANTIATE_TEST_CASE_P( {"hlsl.semicolons.frag", "main"}, {"hlsl.shapeConv.frag", "main"}, {"hlsl.shapeConvRet.frag", "main"}, + {"hlsl.staticMemberFunction.frag", "main"}, {"hlsl.stringtoken.frag", "main"}, {"hlsl.string.frag", "main"}, {"hlsl.struct.split-1.vert", "main"}, diff --git a/hlsl/hlslGrammar.cpp b/hlsl/hlslGrammar.cpp index 89cb8982..a5fc9e6d 100755 --- a/hlsl/hlslGrammar.cpp +++ b/hlsl/hlslGrammar.cpp @@ -298,7 +298,7 @@ bool HlslGrammar::acceptSamplerDeclarationDX9(TType& /*type*/) // bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList) { - bool list = false; + bool declarator_list = false; // true when processing comma separation // attributes TAttributeMap attributes; @@ -319,7 +319,7 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList) // return true; // fully_specified_type - if (! acceptFullySpecifiedType(declaredType)) + if (! acceptFullySpecifiedType(declaredType, nodeList)) return false; // identifier @@ -345,7 +345,7 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList) // compound_statement (function body definition) or just a prototype? if (peekTokenClass(EHTokLeftBrace)) { - if (list) + if (declarator_list) parseContext.error(idToken.loc, "function body can't be in a declarator list", "{", ""); if (typedefDecl) parseContext.error(idToken.loc, "function body can't be in a typedef", "{", ""); @@ -427,7 +427,7 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList) } if (acceptTokenClass(EHTokComma)) { - list = true; + declarator_list = true; continue; } }; @@ -507,6 +507,11 @@ bool HlslGrammar::acceptControlDeclaration(TIntermNode*& node) // | type_qualifier type_specifier // bool HlslGrammar::acceptFullySpecifiedType(TType& type) +{ + TIntermNode* nodeList = nullptr; + return acceptFullySpecifiedType(type, nodeList); +} +bool HlslGrammar::acceptFullySpecifiedType(TType& type, TIntermNode*& nodeList) { // type_qualifier TQualifier qualifier; @@ -516,7 +521,7 @@ bool HlslGrammar::acceptFullySpecifiedType(TType& type) TSourceLoc loc = token.loc; // type_specifier - if (! acceptType(type)) { + if (! acceptType(type, nodeList)) { // If this is not a type, we may have inadvertently gone down a wrong path // by parsing "sample", which can be treated like either an identifier or a // qualifier. Back it out, if we did. @@ -1187,6 +1192,11 @@ bool HlslGrammar::acceptTextureType(TType& type) // and return true and advance. // Otherwise, return false, and don't advance bool HlslGrammar::acceptType(TType& type) +{ + TIntermNode* nodeList = nullptr; + return acceptType(type, nodeList); +} +bool HlslGrammar::acceptType(TType& type, TIntermNode*& nodeList) { // Basic types for min* types, broken out here in case of future // changes, e.g, to use native halfs. @@ -1271,7 +1281,7 @@ bool HlslGrammar::acceptType(TType& type) case EHTokStruct: case EHTokCBuffer: case EHTokTBuffer: - return acceptStruct(type); + return acceptStruct(type, nodeList); case EHTokIdentifier: // An identifier could be for a user-defined type. @@ -1745,7 +1755,7 @@ bool HlslGrammar::acceptType(TType& type) // | CBUFFER // | TBUFFER // -bool HlslGrammar::acceptStruct(TType& type) +bool HlslGrammar::acceptStruct(TType& type, TIntermNode*& nodeList) { // This storage qualifier will tell us whether it's an AST // block type or just a generic structure type. @@ -1788,7 +1798,7 @@ bool HlslGrammar::acceptStruct(TType& type) // struct_declaration_list TTypeList* typeList; - if (! acceptStructDeclarationList(typeList)) { + if (! acceptStructDeclarationList(typeList, nodeList, structName)) { expected("struct member declarations"); return false; } @@ -1913,12 +1923,14 @@ bool HlslGrammar::acceptStructBufferType(TType& type) // // struct_declaration // : fully_specified_type struct_declarator COMMA struct_declarator ... +// | fully_specified_type IDENTIFIER function_parameters post_decls compound_statement // member-function definition // // struct_declarator // : IDENTIFIER post_decls // | IDENTIFIER array_specifier post_decls +// | IDENTIFIER function_parameters post_decls // member-function prototype // -bool HlslGrammar::acceptStructDeclarationList(TTypeList*& typeList) +bool HlslGrammar::acceptStructDeclarationList(TTypeList*& typeList, TIntermNode*& nodeList, const TString& typeName) { typeList = new TTypeList(); HlslToken idToken; @@ -1929,51 +1941,66 @@ bool HlslGrammar::acceptStructDeclarationList(TTypeList*& typeList) return true; // struct_declaration + + bool declarator_list = false; // fully_specified_type TType memberType; - if (! acceptFullySpecifiedType(memberType)) { + if (! acceptFullySpecifiedType(memberType, nodeList)) { expected("member type"); return false; } // struct_declarator COMMA struct_declarator ... + bool functionDefinitionAccepted = false; do { if (! acceptIdentifier(idToken)) { expected("member name"); return false; } - // add it to the list of members - TTypeLoc member = { new TType(EbtVoid), token.loc }; - member.type->shallowCopy(memberType); - member.type->setFieldName(*idToken.string); - typeList->push_back(member); + if (peekTokenClass(EHTokLeftParen)) { + // function_parameters + if (!declarator_list) { + functionDefinitionAccepted = acceptMemberFunctionDefinition(nodeList, typeName, memberType, *idToken.string); + if (functionDefinitionAccepted) + break; + } + expected("member-function definition"); + return false; + } else { + // add it to the list of members + TTypeLoc member = { new TType(EbtVoid), token.loc }; + member.type->shallowCopy(memberType); + member.type->setFieldName(*idToken.string); + typeList->push_back(member); - // array_specifier - TArraySizes* arraySizes = nullptr; - acceptArraySpecifier(arraySizes); - if (arraySizes) - typeList->back().type->newArraySizes(*arraySizes); + // array_specifier + TArraySizes* arraySizes = nullptr; + acceptArraySpecifier(arraySizes); + if (arraySizes) + typeList->back().type->newArraySizes(*arraySizes); - acceptPostDecls(member.type->getQualifier()); + acceptPostDecls(member.type->getQualifier()); - // EQUAL assignment_expression - if (acceptTokenClass(EHTokAssign)) { - parseContext.warn(idToken.loc, "struct-member initializers ignored", "typedef", ""); - TIntermTyped* expressionNode = nullptr; - if (! acceptAssignmentExpression(expressionNode)) { - expected("initializer"); - return false; + // EQUAL assignment_expression + if (acceptTokenClass(EHTokAssign)) { + parseContext.warn(idToken.loc, "struct-member initializers ignored", "typedef", ""); + TIntermTyped* expressionNode = nullptr; + if (! acceptAssignmentExpression(expressionNode)) { + expected("initializer"); + return false; + } } } - // success on seeing the SEMICOLON coming up if (peekTokenClass(EHTokSemicolon)) break; // COMMA - if (! acceptTokenClass(EHTokComma)) { + if (acceptTokenClass(EHTokComma)) + declarator_list = true; + else { expected(","); return false; } @@ -1981,7 +2008,7 @@ bool HlslGrammar::acceptStructDeclarationList(TTypeList*& typeList) } while (true); // SEMI_COLON - if (! acceptTokenClass(EHTokSemicolon)) { + if (! functionDefinitionAccepted && ! acceptTokenClass(EHTokSemicolon)) { expected(";"); return false; } @@ -1989,6 +2016,43 @@ bool HlslGrammar::acceptStructDeclarationList(TTypeList*& typeList) } while (true); } +// member_function_definition +// | function_parameters post_decls compound_statement +// +// Expects type to have EvqGlobal for a static member and +// EvqTemporary for non-static member. +bool HlslGrammar::acceptMemberFunctionDefinition(TIntermNode*& nodeList, const TString& typeName, + const TType& type, const TString& memberName) +{ + // watch early returns... + parseContext.pushThis(typeName); + bool accepted = false; + + TString* functionName = parseContext.getFullMemberFunctionName(memberName, type.getQualifier().storage == EvqGlobal); + TFunction& function = *new TFunction(functionName, type); + + // function_parameters + if (acceptFunctionParameters(function)) { + // post_decls + acceptPostDecls(function.getWritableType().getQualifier()); + + // compound_statement (function body definition) + if (peekTokenClass(EHTokLeftBrace)) { + if (function.getType().getQualifier().storage != EvqGlobal) { + expected("only static member functions are accepted"); + return false; + } + + TAttributeMap attributes; + accepted = acceptFunctionDefinition(function, nodeList, attributes); + } + } else + expected("function parameter list"); + + parseContext.popThis(); + return accepted; +} + // function_parameters // : LEFT_PAREN parameter_declaration COMMA parameter_declaration ... RIGHT_PAREN // | LEFT_PAREN VOID RIGHT_PAREN @@ -2133,6 +2197,7 @@ bool HlslGrammar::acceptFunctionDefinition(TFunction& function, TIntermNode*& no if (! acceptCompoundStatement(functionBody)) return false; + // this does a popScope() parseContext.handleFunctionBody(loc, functionDeclarator, functionBody, functionNode); // Hook up the 1 or 2 function definitions. @@ -2490,17 +2555,21 @@ bool HlslGrammar::acceptUnaryExpression(TIntermTyped*& node) // | postfix_expression LEFT_BRACKET integer_expression RIGHT_BRACKET // | postfix_expression DOT IDENTIFIER // | postfix_expression DOT IDENTIFIER arguments +// | postfix_expression COLONCOLON IDENTIFIER arguments // | postfix_expression INC_OP // | postfix_expression DEC_OP // bool HlslGrammar::acceptPostfixExpression(TIntermTyped*& node) { // Not implemented as self-recursive: - // The logical "right recursion" is done with an loop at the end + // The logical "right recursion" is done with a loop at the end // idToken will pick up either a variable or a function name in a function call HlslToken idToken; + // scopeBase will pick up the type symbol on the left of '::' + TSymbol* scopeBase = nullptr; + // Find something before the postfix operations, as they can't operate // on nothing. So, no "return true", they fall through, only "return false". if (acceptTokenClass(EHTokLeftParen)) { @@ -2518,8 +2587,15 @@ bool HlslGrammar::acceptPostfixExpression(TIntermTyped*& node) } else if (acceptConstructor(node)) { // constructor (nothing else to do yet) } else if (acceptIdentifier(idToken)) { - // identifier or function_call name - if (! peekTokenClass(EHTokLeftParen)) { + // user-type, identifier, or function name + if (peekTokenClass(EHTokColonColon)) { + TType type; + scopeBase = parseContext.lookupUserType(*idToken.string, type); + if (scopeBase == nullptr) { + expected("type left of ::"); + return false; + } + } else if (! peekTokenClass(EHTokLeftParen)) { node = parseContext.handleVariable(idToken.loc, idToken.symbol, idToken.string); } else if (acceptFunctionCall(idToken, node)) { // function_call (nothing else to do yet) @@ -2560,6 +2636,7 @@ bool HlslGrammar::acceptPostfixExpression(TIntermTyped*& node) case EOpIndexIndirect: case EOpPostIncrement: case EOpPostDecrement: + case EOpScoping: advanceToken(); break; default: @@ -2568,6 +2645,7 @@ bool HlslGrammar::acceptPostfixExpression(TIntermTyped*& node) // We have a valid post-unary operator, process it. switch (postOp) { + case EOpScoping: case EOpIndexDirectStruct: { // DOT IDENTIFIER @@ -2583,7 +2661,7 @@ bool HlslGrammar::acceptPostfixExpression(TIntermTyped*& node) TIntermTyped* thisNode = node; // arguments - if (! acceptFunctionCall(field, node, thisNode)) { + if (! acceptFunctionCall(field, node, thisNode, scopeBase)) { expected("function parameters"); return false; } @@ -2655,24 +2733,38 @@ bool HlslGrammar::acceptConstructor(TIntermTyped*& node) // function_call // : [idToken] arguments // -bool HlslGrammar::acceptFunctionCall(HlslToken callToken, TIntermTyped*& node, TIntermTyped* base) +bool HlslGrammar::acceptFunctionCall(HlslToken callToken, TIntermTyped*& node, TIntermTyped* baseObject, + const TSymbol* baseType) { - // arguments - TFunction* function = new TFunction(callToken.string, TType(EbtVoid)); - TIntermTyped* arguments = nullptr; - - // member functions have an implicit first argument of the calling object. - if (base != nullptr) { - if (! parseContext.isBuiltInMethod(callToken.loc, node, *callToken.string)) { - expected("built-in method"); - return false; + // name + TString* functionName = nullptr; + if ((baseObject == nullptr && baseType == nullptr) + || parseContext.isBuiltInMethod(callToken.loc, baseObject, *callToken.string)) + functionName = callToken.string; + else { + functionName = NewPoolTString(""); + if (baseObject != nullptr) { + functionName->append(baseObject->getType().getTypeName().c_str()); + functionName->append("."); + } else if (baseType != nullptr) { + functionName->append(baseType->getType().getTypeName()); + functionName->append("::"); } - parseContext.handleFunctionArgument(function, arguments, base); + functionName->append(*callToken.string); } + // function + TFunction* function = new TFunction(functionName, TType(EbtVoid)); + + // arguments + // Non-static member functions have an implicit first argument of the base object. + TIntermTyped* arguments = nullptr; + if (baseObject != nullptr) + parseContext.handleFunctionArgument(function, arguments, baseObject); if (! acceptArguments(function, arguments)) return false; + // call node = parseContext.handleFunctionCall(callToken.loc, function, arguments); return true; diff --git a/hlsl/hlslGrammar.h b/hlsl/hlslGrammar.h index 8ff4e2e1..6da2ea13 100755 --- a/hlsl/hlslGrammar.h +++ b/hlsl/hlslGrammar.h @@ -69,9 +69,11 @@ namespace glslang { bool acceptSamplerDeclarationDX9(TType&); bool acceptSamplerState(); bool acceptFullySpecifiedType(TType&); + bool acceptFullySpecifiedType(TType&, TIntermNode*& nodeList); bool acceptQualifier(TQualifier&); bool acceptLayoutQualifierList(TQualifier&); bool acceptType(TType&); + bool acceptType(TType&, TIntermNode*& nodeList); bool acceptTemplateVecMatBasicType(TBasicType&); bool acceptVectorTemplateType(TType&); bool acceptMatrixTemplateType(TType&); @@ -83,8 +85,10 @@ namespace glslang { bool acceptSamplerType(TType&); bool acceptTextureType(TType&); bool acceptStructBufferType(TType&); - bool acceptStruct(TType&); - bool acceptStructDeclarationList(TTypeList*&); + bool acceptStruct(TType&, TIntermNode*& nodeList); + bool acceptStructDeclarationList(TTypeList*&, TIntermNode*& nodeList, const TString& typeName); + bool acceptMemberFunctionDefinition(TIntermNode*& nodeList, const TString& typeName, + const TType&, const TString& memberName); bool acceptFunctionParameters(TFunction&); bool acceptParameterDeclaration(TFunction&); bool acceptFunctionDefinition(TFunction&, TIntermNode*& nodeList, const TAttributeMap&); @@ -97,7 +101,8 @@ namespace glslang { bool acceptUnaryExpression(TIntermTyped*&); bool acceptPostfixExpression(TIntermTyped*&); bool acceptConstructor(TIntermTyped*&); - bool acceptFunctionCall(HlslToken, TIntermTyped*&, TIntermTyped* base = nullptr); + bool acceptFunctionCall(HlslToken, TIntermTyped*&, TIntermTyped* objectBase = nullptr, + const TSymbol* typeBase = nullptr); bool acceptArguments(TFunction*, TIntermTyped*&); bool acceptLiteral(TIntermTyped*&); bool acceptCompoundStatement(TIntermNode*&); diff --git a/hlsl/hlslOpMap.cpp b/hlsl/hlslOpMap.cpp index ba56eeb6..ebe6fbd9 100755 --- a/hlsl/hlslOpMap.cpp +++ b/hlsl/hlslOpMap.cpp @@ -118,6 +118,8 @@ TOperator HlslOpMap::postUnary(EHlslTokenClass op) case EHTokIncOp: return EOpPostIncrement; case EHTokDecOp: return EOpPostDecrement; + case EHTokColonColon: return EOpScoping; + default: return EOpNull; // means not a post-unary op } } diff --git a/hlsl/hlslParseHelper.cpp b/hlsl/hlslParseHelper.cpp index a29d1b91..e16865e6 100755 --- a/hlsl/hlslParseHelper.cpp +++ b/hlsl/hlslParseHelper.cpp @@ -958,6 +958,9 @@ TIntermTyped* HlslParseContext::handleDotDereference(const TSourceLoc& loc, TInt // bool HlslParseContext::isBuiltInMethod(const TSourceLoc& loc, TIntermTyped* base, const TString& field) { + if (base == nullptr) + return false; + variableCheck(base); if (base->getType().getBasicType() == EbtSampler) { @@ -7086,6 +7089,49 @@ TIntermNode* HlslParseContext::addSwitch(const TSourceLoc& loc, TIntermTyped* ex return switchNode; } +// Track levels of class/struct nesting with a prefix string using +// the type names separated by the scoping operator. E.g., two levels +// would look like: +// +// outer::inner +// +// The string is empty when at normal global level. +// +void HlslParseContext::pushThis(const TString& typeName) +{ + // make new type prefix + TString newPrefix; + if (currentTypePrefix.size() > 0) { + newPrefix = currentTypePrefix.back(); + newPrefix.append("::"); + } + newPrefix.append(typeName); + currentTypePrefix.push_back(newPrefix); +} + +// Opposite of pushThis(), see above +void HlslParseContext::popThis() +{ + currentTypePrefix.pop_back(); +} + +// Use the class/struct nesting string to create a global name for +// a member of a class/struct. Static members use "::" for the final +// step, while non-static members use ".". +TString* HlslParseContext::getFullMemberFunctionName(const TString& memberName, bool isStatic) const +{ + TString* name = NewPoolTString(""); + if (currentTypePrefix.size() > 0) + name->append(currentTypePrefix.back()); + if (isStatic) + name->append("::"); + else + name->append("."); + name->append(memberName); + + return name; +} + // Potentially rename shader entry point function void HlslParseContext::renameShaderFunction(TString*& name) const { diff --git a/hlsl/hlslParseHelper.h b/hlsl/hlslParseHelper.h index d7a2b785..8f0ea6cb 100755 --- a/hlsl/hlslParseHelper.h +++ b/hlsl/hlslParseHelper.h @@ -160,6 +160,10 @@ public: void pushScope() { symbolTable.push(); } void popScope() { symbolTable.pop(0); } + void pushThis(const TString& name); + void popThis(); + TString* getFullMemberFunctionName(const TString& name, bool isStatic) const; + void pushSwitchSequence(TIntermSequence* sequence) { switchSequenceStack.push_back(sequence); } void popSwitchSequence() { switchSequenceStack.pop_back(); } @@ -382,6 +386,7 @@ protected: TString patchConstantFunctionName; // hull shader patch constant function name, from function level attribute. TMap builtInLinkageSymbols; // used for tessellation, finding declared builtins + TVector currentTypePrefix; }; } // end namespace glslang diff --git a/hlsl/hlslScanContext.cpp b/hlsl/hlslScanContext.cpp index 297f2639..ba8088bc 100755 --- a/hlsl/hlslScanContext.cpp +++ b/hlsl/hlslScanContext.cpp @@ -552,6 +552,8 @@ EHlslTokenClass HlslScanContext::tokenizeClass(HlslToken& token) case PpAtomDecrement: return EHTokDecOp; case PpAtomIncrement: return EHTokIncOp; + case PpAtomColonColon: return EHTokColonColon; + case PpAtomConstInt: parserToken->i = ppToken.ival; return EHTokIntConstant; case PpAtomConstUint: parserToken->i = ppToken.ival; return EHTokUintConstant; case PpAtomConstFloat: parserToken->d = ppToken.dval; return EHTokFloatConstant; diff --git a/hlsl/hlslTokens.h b/hlsl/hlslTokens.h index 9923233a..52496190 100755 --- a/hlsl/hlslTokens.h +++ b/hlsl/hlslTokens.h @@ -267,7 +267,6 @@ enum EHlslTokenClass { // variable, user type, ... EHTokIdentifier, - EHTokTypeName, EHTokClass, EHTokStruct, EHTokCBuffer,