diff --git a/Test/baseResults/hlsl.namespace.frag.out b/Test/baseResults/hlsl.namespace.frag.out new file mode 100755 index 00000000..dae874fc --- /dev/null +++ b/Test/baseResults/hlsl.namespace.frag.out @@ -0,0 +1,180 @@ +hlsl.namespace.frag +Shader version: 450 +gl_FragCoord origin is upper left +0:? Sequence +0:5 Function Definition: N1::getVec( ( temp 4-component vector of float) +0:5 Function Parameters: +0:? Sequence +0:5 Branch: Return with expression +0:5 'v1' ( global 4-component vector of float) +0:10 Function Definition: N2::getVec( ( temp 4-component vector of float) +0:10 Function Parameters: +0:? Sequence +0:10 Branch: Return with expression +0:10 'v2' ( global 4-component vector of float) +0:12 Function Definition: N2::N3::getVec( ( temp 4-component vector of float) +0:12 Function Parameters: +0:? Sequence +0:12 Branch: Return with expression +0:12 'v2' ( global 4-component vector of float) +0:15 Function Definition: N2::N3::C1::getVec( ( temp 4-component vector of float) +0:15 Function Parameters: +0:15 '@this' ( temp structure{}) +0:? Sequence +0:15 Branch: Return with expression +0:15 'v2' ( global 4-component vector of float) +0:21 Function Definition: @main( ( temp 4-component vector of float) +0:21 Function Parameters: +0:? Sequence +0:22 Branch: Return with expression +0:22 add ( temp 4-component vector of float) +0:22 add ( temp 4-component vector of float) +0:22 add ( temp 4-component vector of float) +0:22 Function Call: N1::getVec( ( temp 4-component vector of float) +0:22 Function Call: N2::getVec( ( temp 4-component vector of float) +0:22 Function Call: N2::N3::getVec( ( temp 4-component vector of float) +0:22 vector-scale ( temp 4-component vector of float) +0:22 Function Call: N2::N3::C1::getVec( ( temp 4-component vector of float) +0:22 'N2::gf' ( global float) +0:21 Function Definition: main( ( temp void) +0:21 Function Parameters: +0:? Sequence +0:21 move second child to first child ( temp 4-component vector of float) +0:? '@entryPointOutput' (layout( location=0) out 4-component vector of float) +0:21 Function Call: @main( ( temp 4-component vector of float) +0:? Linker Objects +0:? 'v1' ( global 4-component vector of float) +0:? 'v2' ( global 4-component vector of float) +0:? 'N2::gf' ( global float) +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: N1::getVec( ( temp 4-component vector of float) +0:5 Function Parameters: +0:? Sequence +0:5 Branch: Return with expression +0:5 'v1' ( global 4-component vector of float) +0:10 Function Definition: N2::getVec( ( temp 4-component vector of float) +0:10 Function Parameters: +0:? Sequence +0:10 Branch: Return with expression +0:10 'v2' ( global 4-component vector of float) +0:12 Function Definition: N2::N3::getVec( ( temp 4-component vector of float) +0:12 Function Parameters: +0:? Sequence +0:12 Branch: Return with expression +0:12 'v2' ( global 4-component vector of float) +0:15 Function Definition: N2::N3::C1::getVec( ( temp 4-component vector of float) +0:15 Function Parameters: +0:15 '@this' ( temp structure{}) +0:? Sequence +0:15 Branch: Return with expression +0:15 'v2' ( global 4-component vector of float) +0:21 Function Definition: @main( ( temp 4-component vector of float) +0:21 Function Parameters: +0:? Sequence +0:22 Branch: Return with expression +0:22 add ( temp 4-component vector of float) +0:22 add ( temp 4-component vector of float) +0:22 add ( temp 4-component vector of float) +0:22 Function Call: N1::getVec( ( temp 4-component vector of float) +0:22 Function Call: N2::getVec( ( temp 4-component vector of float) +0:22 Function Call: N2::N3::getVec( ( temp 4-component vector of float) +0:22 vector-scale ( temp 4-component vector of float) +0:22 Function Call: N2::N3::C1::getVec( ( temp 4-component vector of float) +0:22 'N2::gf' ( global float) +0:21 Function Definition: main( ( temp void) +0:21 Function Parameters: +0:? Sequence +0:21 move second child to first child ( temp 4-component vector of float) +0:? '@entryPointOutput' (layout( location=0) out 4-component vector of float) +0:21 Function Call: @main( ( temp 4-component vector of float) +0:? Linker Objects +0:? 'v1' ( global 4-component vector of float) +0:? 'v2' ( global 4-component vector of float) +0:? 'N2::gf' ( global float) +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 9 "N1::getVec(" + Name 11 "N2::getVec(" + Name 13 "N2::N3::getVec(" + Name 15 "C1" + Name 19 "N2::N3::C1::getVec(" + Name 18 "@this" + Name 21 "@main(" + Name 24 "v1" + Name 28 "v2" + Name 45 "N2::gf" + Name 52 "@entryPointOutput" + Decorate 52(@entryPointOutput) Location 0 + 2: TypeVoid + 3: TypeFunction 2 + 6: TypeFloat 32 + 7: TypeVector 6(float) 4 + 8: TypeFunction 7(fvec4) + 15(C1): TypeStruct + 16: TypePointer Function 15(C1) + 17: TypeFunction 7(fvec4) 16(ptr) + 23: TypePointer Private 7(fvec4) + 24(v1): 23(ptr) Variable Private + 28(v2): 23(ptr) Variable Private + 44: TypePointer Private 6(float) + 45(N2::gf): 44(ptr) Variable Private + 51: TypePointer Output 7(fvec4) +52(@entryPointOutput): 51(ptr) Variable Output + 4(main): 2 Function None 3 + 5: Label + 53: 7(fvec4) FunctionCall 21(@main() + Store 52(@entryPointOutput) 53 + Return + FunctionEnd + 9(N1::getVec(): 7(fvec4) Function None 8 + 10: Label + 25: 7(fvec4) Load 24(v1) + ReturnValue 25 + FunctionEnd + 11(N2::getVec(): 7(fvec4) Function None 8 + 12: Label + 29: 7(fvec4) Load 28(v2) + ReturnValue 29 + FunctionEnd +13(N2::N3::getVec(): 7(fvec4) Function None 8 + 14: Label + 32: 7(fvec4) Load 28(v2) + ReturnValue 32 + FunctionEnd +19(N2::N3::C1::getVec(): 7(fvec4) Function None 17 + 18(@this): 16(ptr) FunctionParameter + 20: Label + 35: 7(fvec4) Load 28(v2) + ReturnValue 35 + FunctionEnd + 21(@main(): 7(fvec4) Function None 8 + 22: Label + 38: 7(fvec4) FunctionCall 9(N1::getVec() + 39: 7(fvec4) FunctionCall 11(N2::getVec() + 40: 7(fvec4) FAdd 38 39 + 41: 7(fvec4) FunctionCall 13(N2::N3::getVec() + 42: 7(fvec4) FAdd 40 41 + 43: 7(fvec4) FunctionCall 19(N2::N3::C1::getVec() + 46: 6(float) Load 45(N2::gf) + 47: 7(fvec4) VectorTimesScalar 43 46 + 48: 7(fvec4) FAdd 42 47 + ReturnValue 48 + FunctionEnd diff --git a/Test/hlsl.namespace.frag b/Test/hlsl.namespace.frag new file mode 100755 index 00000000..76c3062d --- /dev/null +++ b/Test/hlsl.namespace.frag @@ -0,0 +1,23 @@ +static float4 v1; +static float4 v2; + +namespace N1 { + float4 getVec() { return v1; } +} + +namespace N2 { + static float gf; + float4 getVec() { return v2; } + namespace N3 { + float4 getVec() { return v2; } + + class C1 { + float4 getVec() { return v2; } + }; + } +} + +float4 main() : SV_Target0 +{ + return N1::getVec() + N2::getVec() + N2::N3::getVec() + N2::N3::C1::getVec() * N2::gf; +} diff --git a/glslang/Include/revision.h b/glslang/Include/revision.h index f6389098..5a18fcbe 100644 --- a/glslang/Include/revision.h +++ b/glslang/Include/revision.h @@ -2,5 +2,5 @@ // For the version, it uses the latest git tag followed by the number of commits. // For the date, it uses the current date (when then script is run). -#define GLSLANG_REVISION "Overload400-PrecQual.1939" +#define GLSLANG_REVISION "Overload400-PrecQual.1946" #define GLSLANG_DATE "30-Mar-2017" diff --git a/gtests/Hlsl.FromFile.cpp b/gtests/Hlsl.FromFile.cpp index 185435a9..2b775e86 100644 --- a/gtests/Hlsl.FromFile.cpp +++ b/gtests/Hlsl.FromFile.cpp @@ -161,6 +161,7 @@ INSTANTIATE_TEST_CASE_P( {"hlsl.logical.unary.frag", "main"}, {"hlsl.logical.binary.frag", "main"}, {"hlsl.logical.binary.vec.frag", "main"}, + {"hlsl.namespace.frag", "main"}, {"hlsl.matNx1.frag", "main"}, {"hlsl.matrixSwizzle.vert", "ShaderFunction"}, {"hlsl.mintypes.frag", "main"}, diff --git a/hlsl/hlslGrammar.cpp b/hlsl/hlslGrammar.cpp index 155a5b81..dd9cbf6d 100755 --- a/hlsl/hlslGrammar.cpp +++ b/hlsl/hlslGrammar.cpp @@ -134,22 +134,17 @@ bool HlslGrammar::acceptIdentifier(HlslToken& idToken) } // compilationUnit -// : list of externalDeclaration -// | SEMICOLONS +// : declaration_list EOF // bool HlslGrammar::acceptCompilationUnit() { TIntermNode* unitNode = nullptr; - while (! peekTokenClass(EHTokNone)) { - // HLSL allows semicolons between global declarations, e.g, between functions. - if (acceptTokenClass(EHTokSemicolon)) - continue; + if (! acceptDeclarationList(unitNode)) + return false; - // externalDeclaration - if (! acceptDeclaration(unitNode)) - return false; - } + if (! peekTokenClass(EHTokNone)) + return false; // set root of AST if (unitNode && !unitNode->getAsAggregate()) @@ -159,6 +154,34 @@ bool HlslGrammar::acceptCompilationUnit() return true; } +// Recognize the following, but with the extra condition that it can be +// successfully terminated by EOF or '}'. +// +// declaration_list +// : list of declaration_or_semicolon followed by EOF or RIGHT_BRACE +// +// declaration_or_semicolon +// : declaration +// : SEMICOLON +// +bool HlslGrammar::acceptDeclarationList(TIntermNode*& nodeList) +{ + do { + // HLSL allows extra semicolons between global declarations + do { } while (acceptTokenClass(EHTokSemicolon)); + + // EOF or RIGHT_BRACE + if (peekTokenClass(EHTokNone) || peekTokenClass(EHTokRightBrace)) + return true; + + // declaration + if (! acceptDeclaration(nodeList)) + return false; + } while (true); + + return true; +} + // sampler_state // : LEFT_BRACE [sampler_state_assignment ... ] RIGHT_BRACE // @@ -289,6 +312,7 @@ bool HlslGrammar::acceptSamplerDeclarationDX9(TType& /*type*/) // | fully_specified_type identifier function_parameters post_decls compound_statement // function definition // | fully_specified_type identifier sampler_state post_decls compound_statement // sampler definition // | typedef declaration +// | NAMESPACE IDENTIFIER LEFT_BRACE declaration_list RIGHT_BRACE // // declarator_list // : declarator COMMA declarator COMMA declarator... // zero or more declarators @@ -314,6 +338,30 @@ bool HlslGrammar::acceptSamplerDeclarationDX9(TType& /*type*/) // bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList) { + // NAMESPACE IDENTIFIER LEFT_BRACE declaration_list RIGHT_BRACE + if (acceptTokenClass(EHTokNamespace)) { + HlslToken namespaceToken; + if (!acceptIdentifier(namespaceToken)) { + expected("namespace name"); + return false; + } + parseContext.pushNamespace(*namespaceToken.string); + if (!acceptTokenClass(EHTokLeftBrace)) { + expected("{"); + return false; + } + if (!acceptDeclarationList(nodeList)) { + expected("declaration list"); + return false; + } + if (!acceptTokenClass(EHTokRightBrace)) { + expected("}"); + return false; + } + parseContext.popNamespace(); + return true; + } + bool declarator_list = false; // true when processing comma separation // attributes @@ -342,15 +390,17 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList) HlslToken idToken; TIntermAggregate* initializers = nullptr; while (acceptIdentifier(idToken)) { + const TString *fullName = idToken.string; + if (parseContext.symbolTable.atGlobalLevel()) + parseContext.getFullNamespaceName(fullName); if (peekTokenClass(EHTokLeftParen)) { // looks like function parameters - const TString* fnName = idToken.string; // Potentially rename shader entry point function. No-op most of the time. - parseContext.renameShaderFunction(fnName); + parseContext.renameShaderFunction(fullName); // function_parameters - declarator.function = new TFunction(fnName, declaredType); + declarator.function = new TFunction(fullName, declaredType); if (!acceptFunctionParameters(*declarator.function)) { expected("function parameter list"); return false; @@ -424,19 +474,19 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList) // TODO: strings are not yet handled. if (variableType.getBasicType() != EbtString && parseContext.getAnnotationNestingLevel() == 0) { if (typedefDecl) - parseContext.declareTypedef(idToken.loc, *idToken.string, variableType); + parseContext.declareTypedef(idToken.loc, *fullName, variableType); else if (variableType.getBasicType() == EbtBlock) - parseContext.declareBlock(idToken.loc, variableType, idToken.string); + parseContext.declareBlock(idToken.loc, variableType, fullName); else { if (variableType.getQualifier().storage == EvqUniform && ! variableType.containsOpaque()) { // this isn't really an individual variable, but a member of the $Global buffer - parseContext.growGlobalUniformBlock(idToken.loc, variableType, *idToken.string); + parseContext.growGlobalUniformBlock(idToken.loc, variableType, *fullName); } else { // Declare the variable and add any initializer code to the AST. // The top-level node is always made into an aggregate, as that's // historically how the AST has been. initializers = intermediate.growAggregate(initializers, - parseContext.declareVariable(idToken.loc, *idToken.string, variableType, expressionNode), + parseContext.declareVariable(idToken.loc, *fullName, variableType, expressionNode), idToken.loc); } } @@ -2624,12 +2674,12 @@ bool HlslGrammar::acceptUnaryExpression(TIntermTyped*& node) // : LEFT_PAREN expression RIGHT_PAREN // | literal // | constructor -// | identifier +// | IDENTIFIER [ COLONCOLON IDENTIFIER [ COLONCOLON IDENTIFIER ... ] ] // | function_call // | postfix_expression LEFT_BRACKET integer_expression RIGHT_BRACKET // | postfix_expression DOT IDENTIFIER // | postfix_expression DOT IDENTIFIER arguments -// | postfix_expression COLONCOLON IDENTIFIER arguments +// | postfix_expression arguments // | postfix_expression INC_OP // | postfix_expression DEC_OP // @@ -2641,9 +2691,6 @@ bool HlslGrammar::acceptPostfixExpression(TIntermTyped*& node) // 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* scope = 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)) { @@ -2657,21 +2704,26 @@ bool HlslGrammar::acceptPostfixExpression(TIntermTyped*& node) return false; } } else if (acceptLiteral(node)) { - // literal (nothing else to do yet), go on to the + // literal (nothing else to do yet) } else if (acceptConstructor(node)) { // constructor (nothing else to do yet) } else if (acceptIdentifier(idToken)) { - // user-type, identifier, or function name - if (peekTokenClass(EHTokColonColon)) { - TType type; - scope = parseContext.lookupUserType(*idToken.string, type); - if (scope == nullptr) { - expected("type left of ::"); + // user-type, namespace name, variable, or function name + TString* fullName = idToken.string; + while (acceptTokenClass(EHTokColonColon)) { + // user-type or namespace name + fullName = NewPoolTString(fullName->c_str()); + fullName->append(parseContext.scopeMangler); + if (acceptIdentifier(idToken)) + fullName->append(*idToken.string); + else { + expected("identifier after ::"); return false; } - } else if (! peekTokenClass(EHTokLeftParen)) { - node = parseContext.handleVariable(idToken.loc, idToken.string); - } else if (acceptFunctionCall(idToken, node)) { + } + if (! peekTokenClass(EHTokLeftParen)) { + node = parseContext.handleVariable(idToken.loc, fullName); + } else if (acceptFunctionCall(idToken.loc, *fullName, node, nullptr)) { // function_call (nothing else to do yet) } else { expected("function call arguments"); @@ -2735,7 +2787,7 @@ bool HlslGrammar::acceptPostfixExpression(TIntermTyped*& node) TIntermTyped* thisNode = node; // arguments - if (! acceptFunctionCall(field, node, thisNode, scope)) { + if (! acceptFunctionCall(field.loc, *field.string, node, thisNode)) { expected("function parameters"); return false; } @@ -2807,26 +2859,26 @@ bool HlslGrammar::acceptConstructor(TIntermTyped*& node) // function_call // : [idToken] arguments // -bool HlslGrammar::acceptFunctionCall(HlslToken callToken, TIntermTyped*& node, TIntermTyped* baseObject, - const TSymbol* scope) +bool HlslGrammar::acceptFunctionCall(const TSourceLoc& loc, TString& name, TIntermTyped*& node, TIntermTyped* baseObject) { // name TString* functionName = nullptr; - if ((baseObject == nullptr && scope == nullptr)) { - functionName = callToken.string; - } else if (parseContext.isBuiltInMethod(callToken.loc, baseObject, *callToken.string)) { + if (baseObject == nullptr) { + functionName = &name; + } else if (parseContext.isBuiltInMethod(loc, baseObject, name)) { // Built-in methods are not in the symbol table as methods, but as global functions // taking an explicit 'this' as the first argument. functionName = NewPoolTString(BUILTIN_PREFIX); - functionName->append(*callToken.string); + functionName->append(name); } else { + if (! baseObject->getType().isStruct()) { + expected("structure"); + return false; + } functionName = NewPoolTString(""); - if (baseObject != nullptr) - functionName->append(baseObject->getType().getTypeName()); - else if (scope != nullptr) - functionName->append(scope->getType().getTypeName()); + functionName->append(baseObject->getType().getTypeName()); parseContext.addScopeMangler(*functionName); - functionName->append(*callToken.string); + functionName->append(name); } // function @@ -2842,7 +2894,7 @@ bool HlslGrammar::acceptFunctionCall(HlslToken callToken, TIntermTyped*& node, T return false; // call - node = parseContext.handleFunctionCall(callToken.loc, function, arguments); + node = parseContext.handleFunctionCall(loc, function, arguments); return true; } diff --git a/hlsl/hlslGrammar.h b/hlsl/hlslGrammar.h index 38b2b584..e88d7805 100755 --- a/hlsl/hlslGrammar.h +++ b/hlsl/hlslGrammar.h @@ -65,6 +65,7 @@ namespace glslang { void unimplemented(const char*); bool acceptIdentifier(HlslToken&); bool acceptCompilationUnit(); + bool acceptDeclarationList(TIntermNode*&); bool acceptDeclaration(TIntermNode*&); bool acceptControlDeclaration(TIntermNode*& node); bool acceptSamplerDeclarationDX9(TType&); @@ -103,8 +104,7 @@ namespace glslang { bool acceptUnaryExpression(TIntermTyped*&); bool acceptPostfixExpression(TIntermTyped*&); bool acceptConstructor(TIntermTyped*&); - bool acceptFunctionCall(HlslToken, TIntermTyped*&, TIntermTyped* objectBase = nullptr, - const TSymbol* scope = nullptr); + bool acceptFunctionCall(const TSourceLoc&, TString& name, TIntermTyped*&, TIntermTyped* objectBase); bool acceptArguments(TFunction*, TIntermTyped*&); bool acceptLiteral(TIntermTyped*&); bool acceptCompoundStatement(TIntermNode*&); diff --git a/hlsl/hlslScanContext.cpp b/hlsl/hlslScanContext.cpp index 7b0365f4..6ddb39d2 100755 --- a/hlsl/hlslScanContext.cpp +++ b/hlsl/hlslScanContext.cpp @@ -334,6 +334,7 @@ void HlslScanContext::fillInKeywordMap() (*KeywordMap)["tbuffer"] = EHTokTBuffer; (*KeywordMap)["typedef"] = EHTokTypedef; (*KeywordMap)["this"] = EHTokThis; + (*KeywordMap)["namespace"] = EHTokNamespace; (*KeywordMap)["true"] = EHTokBoolConstant; (*KeywordMap)["false"] = EHTokBoolConstant; @@ -828,6 +829,7 @@ EHlslTokenClass HlslScanContext::tokenizeIdentifier() case EHTokCBuffer: case EHTokTBuffer: case EHTokThis: + case EHTokNamespace: return keyword; case EHTokBoolConstant: diff --git a/hlsl/hlslTokens.h b/hlsl/hlslTokens.h index 9f91906c..cba0b965 100755 --- a/hlsl/hlslTokens.h +++ b/hlsl/hlslTokens.h @@ -274,6 +274,7 @@ enum EHlslTokenClass { EHTokTBuffer, EHTokTypedef, EHTokThis, + EHTokNamespace, // constant EHTokFloatConstant,