From 7be4b8282dcb19f34e0b08c34d9883e56e8b1c8d Mon Sep 17 00:00:00 2001 From: Dejan Mircevski Date: Wed, 17 Jun 2015 11:40:33 -0400 Subject: [PATCH 1/5] Add #include processing to glslang (though turned off by default). When an include directive is recognized by the preprocessor, it executes a callback on the filepath argument to obtain the file contents. That way the compilation client can deal with the file system, include paths, etc. Currently only accepts quoted filepaths -- no angle brackets yet. --- StandAlone/StandAlone.cpp | 4 +-- .../baseResults/preprocessor.include.vert.err | 8 +++++ .../baseResults/preprocessor.include.vert.out | 0 Test/preprocessor.include.vert | 6 ++++ Test/test-preprocessor-list | 1 + glslang/MachineIndependent/ParseHelper.h | 1 + glslang/MachineIndependent/Scan.h | 3 ++ glslang/MachineIndependent/ShaderLang.cpp | 33 ++++++++++------- .../MachineIndependent/preprocessor/Pp.cpp | 35 +++++++++++++++++++ .../preprocessor/PpAtom.cpp | 3 ++ .../preprocessor/PpContext.cpp | 4 +-- .../preprocessor/PpContext.h | 35 ++++++++++++++++++- .../preprocessor/PpTokens.h | 3 ++ glslang/Public/ShaderLang.h | 27 ++++++++++++-- 14 files changed, 142 insertions(+), 21 deletions(-) create mode 100644 Test/baseResults/preprocessor.include.vert.err create mode 100644 Test/baseResults/preprocessor.include.vert.out create mode 100644 Test/preprocessor.include.vert diff --git a/StandAlone/StandAlone.cpp b/StandAlone/StandAlone.cpp index b88d8ad5..cc6e279b 100644 --- a/StandAlone/StandAlone.cpp +++ b/StandAlone/StandAlone.cpp @@ -700,8 +700,8 @@ void CompileAndLinkShaders() shader->setStrings(shaderStrings, 1); if (Options & EOptionOutputPreprocessed) { std::string str; - if (shader->preprocess(&Resources, defaultVersion, ENoProfile, - false, false, messages, &str)) { + if (shader->preprocess(&Resources, defaultVersion, ENoProfile, false, false, + messages, &str, glslang::TShader::ForbidInclude())) { PutsIfNonEmpty(str.c_str()); } else { CompileFailed = true; diff --git a/Test/baseResults/preprocessor.include.vert.err b/Test/baseResults/preprocessor.include.vert.err new file mode 100644 index 00000000..edc9ba9d --- /dev/null +++ b/Test/baseResults/preprocessor.include.vert.err @@ -0,0 +1,8 @@ +ERROR: 0:8000: '#include' : must be followed by a file designation +ERROR: 0:8001: '#include' : must be followed by a file designation +ERROR: 0:8002: '#error' : unexpected include directive +ERROR: 0:8003: '#include' : extra content after file designation +ERROR: 0:8004: '#error' : unexpected include directive +ERROR: 5 compilation errors. No code generated. + + diff --git a/Test/baseResults/preprocessor.include.vert.out b/Test/baseResults/preprocessor.include.vert.out new file mode 100644 index 00000000..e69de29b diff --git a/Test/preprocessor.include.vert b/Test/preprocessor.include.vert new file mode 100644 index 00000000..512327ba --- /dev/null +++ b/Test/preprocessor.include.vert @@ -0,0 +1,6 @@ +#line 8000 +#include +#include 123 +#include "foo" +#include "foo" garbage +#include "no-eol" \ No newline at end of file diff --git a/Test/test-preprocessor-list b/Test/test-preprocessor-list index e75ac1b4..195b4a79 100644 --- a/Test/test-preprocessor-list +++ b/Test/test-preprocessor-list @@ -4,6 +4,7 @@ preprocessor.edge_cases.vert preprocessor.errors.vert preprocessor.extensions.vert preprocessor.function_macro.vert +preprocessor.include.vert preprocessor.line.vert preprocessor.line.frag preprocessor.pragma.vert diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h index 6f5645ef..f0c63bb3 100644 --- a/glslang/MachineIndependent/ParseHelper.h +++ b/glslang/MachineIndependent/ParseHelper.h @@ -214,6 +214,7 @@ public: int getNumErrors() const { return numErrors; } const TSourceLoc& getCurrentLoc() const { return currentScanner->getSourceLoc(); } void setCurrentLine(int line) { currentScanner->setLine(line); } + void setCurrentColumn(int col) { currentScanner->setColumn(col); } void setCurrentSourceName(const char* name) { currentScanner->setFile(name); } void setCurrentString(int string) { currentScanner->setString(string); } void setScanner(TInputScanner* scanner) { currentScanner = scanner; } diff --git a/glslang/MachineIndependent/Scan.h b/glslang/MachineIndependent/Scan.h index ff7bfa2f..eade05fd 100644 --- a/glslang/MachineIndependent/Scan.h +++ b/glslang/MachineIndependent/Scan.h @@ -154,6 +154,9 @@ public: loc[getLastValidSourceIndex()].name = nullptr; } + // for #include content indentation + void setColumn(int col) { loc[getLastValidSourceIndex()].column = col; } + const TSourceLoc& getSourceLoc() const { return loc[std::max(0, std::min(currentSource, numSources - finale - 1))]; } // Returns the index (starting from 0) of the most recent valid source string we are reading from. int getLastValidSourceIndex() const { return std::min(currentSource, numSources - 1); } diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp index e98df614..617f9e21 100644 --- a/glslang/MachineIndependent/ShaderLang.cpp +++ b/glslang/MachineIndependent/ShaderLang.cpp @@ -130,7 +130,7 @@ bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profil TIntermediate intermediate(language, version, profile); TParseContext parseContext(symbolTable, intermediate, true, version, profile, language, infoSink); - TPpContext ppContext(parseContext); + TPpContext ppContext(parseContext, TShader::ForbidInclude()); TScanContext scanContext(parseContext); parseContext.setScanContext(&scanContext); parseContext.setPpContext(&ppContext); @@ -463,7 +463,8 @@ bool ProcessDeferred( EShMessages messages, // warnings/errors/AST; things to print out TIntermediate& intermediate, // returned tree, etc. ProcessingContext& processingContext, - bool requireNonempty + bool requireNonempty, + const TShader::Includer& includer ) { if (! InitThread()) @@ -565,7 +566,7 @@ bool ProcessDeferred( TParseContext parseContext(symbolTable, intermediate, false, version, profile, compiler->getLanguage(), compiler->infoSink, forwardCompatible, messages); glslang::TScanContext scanContext(parseContext); - TPpContext ppContext(parseContext); + TPpContext ppContext(parseContext, includer); parseContext.setScanContext(&scanContext); parseContext.setPpContext(&ppContext); parseContext.setLimits(*resources); @@ -838,6 +839,7 @@ bool PreprocessDeferred( bool forceDefaultVersionAndProfile, bool forwardCompatible, // give errors for use of deprecated features EShMessages messages, // warnings/errors/AST; things to print out + const TShader::Includer& includer, TIntermediate& intermediate, // returned tree, etc. std::string* outputString) { @@ -845,7 +847,8 @@ bool PreprocessDeferred( return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames, preamble, optLevel, resources, defaultVersion, defaultProfile, forceDefaultVersionAndProfile, - forwardCompatible, messages, intermediate, parser, false); + forwardCompatible, messages, intermediate, parser, + false, includer); } @@ -874,13 +877,15 @@ bool CompileDeferred( bool forceDefaultVersionAndProfile, bool forwardCompatible, // give errors for use of deprecated features EShMessages messages, // warnings/errors/AST; things to print out - TIntermediate& intermediate) // returned tree, etc. + TIntermediate& intermediate,// returned tree, etc. + const TShader::Includer& includer) { DoFullParse parser; return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames, preamble, optLevel, resources, defaultVersion, defaultProfile, forceDefaultVersionAndProfile, - forwardCompatible, messages, intermediate, parser, true); + forwardCompatible, messages, intermediate, parser, + true, includer); } } // end anonymous namespace for local functions @@ -1024,7 +1029,7 @@ int ShCompile( TIntermediate intermediate(compiler->getLanguage()); bool success = CompileDeferred(compiler, shaderStrings, numStrings, inputLengths, nullptr, "", optLevel, resources, defaultVersion, ENoProfile, false, - forwardCompatible, messages, intermediate); + forwardCompatible, messages, intermediate, TShader::ForbidInclude()); // // Call the machine dependent compiler @@ -1327,7 +1332,7 @@ void TShader::setStringsWithLengthsAndNames( // Returns true for success. // bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, - bool forwardCompatible, EShMessages messages) + bool forwardCompatible, EShMessages messages, const Includer& includer) { if (! InitThread()) return false; @@ -1340,7 +1345,7 @@ bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion return CompileDeferred(compiler, strings, numStrings, lengths, stringNames, preamble, EShOptNone, builtInResources, defaultVersion, defaultProfile, forceDefaultVersionAndProfile, - forwardCompatible, messages, *intermediate); + forwardCompatible, messages, *intermediate, includer); } bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion, bool forwardCompatible, EShMessages messages) @@ -1351,9 +1356,11 @@ bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion // Fill in a string with the result of preprocessing ShaderStrings // Returns true if all extensions, pragmas and version strings were valid. bool TShader::preprocess(const TBuiltInResource* builtInResources, - int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, - bool forwardCompatible, - EShMessages message, std::string* output_string) + int defaultVersion, EProfile defaultProfile, + bool forceDefaultVersionAndProfile, + bool forwardCompatible, EShMessages message, + std::string* output_string, + const TShader::Includer& includer) { if (! InitThread()) return false; @@ -1366,7 +1373,7 @@ bool TShader::preprocess(const TBuiltInResource* builtInResources, return PreprocessDeferred(compiler, strings, numStrings, lengths, stringNames, preamble, EShOptNone, builtInResources, defaultVersion, defaultProfile, forceDefaultVersionAndProfile, - forwardCompatible, message, *intermediate, output_string); + forwardCompatible, message, includer, *intermediate, output_string); } const char* TShader::getInfoLog() diff --git a/glslang/MachineIndependent/preprocessor/Pp.cpp b/glslang/MachineIndependent/preprocessor/Pp.cpp index 279706ec..9e126824 100644 --- a/glslang/MachineIndependent/preprocessor/Pp.cpp +++ b/glslang/MachineIndependent/preprocessor/Pp.cpp @@ -596,6 +596,38 @@ int TPpContext::CPPifdef(int defined, TPpToken* ppToken) return token; } +// Handle #include +int TPpContext::CPPinclude(TPpToken* ppToken) +{ + const TSourceLoc directiveLoc = ppToken->loc; + int token = scanToken(ppToken); + if (token != PpAtomConstString) { + // TODO: handle angle brackets. + parseContext.ppError(directiveLoc, "must be followed by a file designation", "#include", ""); + } else { + const char* name = GetAtomString(ppToken->atom); + token = scanToken(ppToken); + if (token != '\n' && token != EndOfInput) { + parseContext.ppError(ppToken->loc, "extra content after file designation", "#include", ""); + } else { + if (!inputStack.empty()) ungetChar(); + std::string replacement; + bool success; + std::tie(success, replacement) = includer.include(name); + if (success) { + pushInput(new TokenizableString(replacement, this)); + // At EOF, there's no "current" location anymore. + if (token != EndOfInput) parseContext.setCurrentColumn(0); + // Don't accidentally return EndOfInput, which will end all preprocessing. + return '\n'; + } else { + parseContext.ppError(ppToken->loc, "not found", name, ""); + } + } + } + return token; +} + // Handle #line int TPpContext::CPPline(TPpToken* ppToken) { @@ -845,6 +877,9 @@ int TPpContext::readCPPline(TPpToken* ppToken) case PpAtomIfndef: token = CPPifdef(0, ppToken); break; + case PpAtomInclude: + token = CPPinclude(ppToken); + break; case PpAtomLine: token = CPPline(ppToken); break; diff --git a/glslang/MachineIndependent/preprocessor/PpAtom.cpp b/glslang/MachineIndependent/preprocessor/PpAtom.cpp index 04e0d630..b3cb2b07 100644 --- a/glslang/MachineIndependent/preprocessor/PpAtom.cpp +++ b/glslang/MachineIndependent/preprocessor/PpAtom.cpp @@ -120,6 +120,9 @@ const struct { { PpAtomLineMacro, "__LINE__" }, { PpAtomFileMacro, "__FILE__" }, { PpAtomVersionMacro, "__VERSION__" }, + + { PpAtomInclude, "include" }, + }; } // end anonymous namespace diff --git a/glslang/MachineIndependent/preprocessor/PpContext.cpp b/glslang/MachineIndependent/preprocessor/PpContext.cpp index 828764b0..b8d2c737 100644 --- a/glslang/MachineIndependent/preprocessor/PpContext.cpp +++ b/glslang/MachineIndependent/preprocessor/PpContext.cpp @@ -83,8 +83,8 @@ NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace glslang { -TPpContext::TPpContext(TParseContext& pc) : - preamble(0), strings(0), parseContext(pc), inComment(false) +TPpContext::TPpContext(TParseContext& pc, const TShader::Includer& inclr) : + preamble(0), strings(0), parseContext(pc), includer(inclr), inComment(false) { InitAtomTable(); InitScanner(); diff --git a/glslang/MachineIndependent/preprocessor/PpContext.h b/glslang/MachineIndependent/preprocessor/PpContext.h index 74a9f5dd..82564ebc 100644 --- a/glslang/MachineIndependent/preprocessor/PpContext.h +++ b/glslang/MachineIndependent/preprocessor/PpContext.h @@ -118,7 +118,7 @@ class TInputScanner; // Don't expect too much in terms of OO design. class TPpContext { public: - TPpContext(TParseContext&); + TPpContext(TParseContext&, const TShader::Includer&); virtual ~TPpContext(); void setPreamble(const char* preamble, size_t length); @@ -281,6 +281,8 @@ protected: // from Pp.cpp // TSourceLoc ifloc; /* outermost #if */ + // Used to obtain #include content. + const TShader::Includer& includer; int InitCPP(); int CPPdefine(TPpToken * ppToken); @@ -291,6 +293,7 @@ protected: int evalToToken(int token, bool shortCircuit, int& res, bool& err, TPpToken * ppToken); int CPPif (TPpToken * ppToken); int CPPifdef(int defined, TPpToken * ppToken); + int CPPinclude(TPpToken * ppToken); int CPPline(TPpToken * ppToken); int CPPerror(TPpToken * ppToken); int CPPpragma(TPpToken * ppToken); @@ -419,6 +422,36 @@ protected: TInputScanner* input; }; + // Holds a string that can be tokenized via the tInput interface. + class TokenizableString : public tInput { + public: + // Copies str, which must be non-empty. + TokenizableString(const std::string& str, TPpContext* pp) + : tInput(pp), + str_(str), + strings(str_.data()), + length(str_.size()), + scanner(1, &strings, &length), + stringInput(pp, scanner) {} + + // tInput methods: + int scan(TPpToken* t) override { return stringInput.scan(t); } + int getch() override { return stringInput.getch(); } + void ungetch() override { stringInput.ungetch(); } + + private: + // Stores the titular string. + const std::string str_; + // Will point to str_[0] and be passed to scanner constructor. + const char* const strings; + // Length of str_, passed to scanner constructor. + size_t length; + // Scans over str_. + TInputScanner scanner; + // Delegate object implementing the tInput interface. + tStringInput stringInput; + }; + int InitScanner(); int ScanFromString(char* s); void missingEndifCheck(); diff --git a/glslang/MachineIndependent/preprocessor/PpTokens.h b/glslang/MachineIndependent/preprocessor/PpTokens.h index 0d116f9b..391b04ad 100644 --- a/glslang/MachineIndependent/preprocessor/PpTokens.h +++ b/glslang/MachineIndependent/preprocessor/PpTokens.h @@ -158,6 +158,9 @@ enum EFixedAtoms { PpAtomFileMacro, PpAtomVersionMacro, + // #include + PpAtomInclude, + PpAtomLast, }; diff --git a/glslang/Public/ShaderLang.h b/glslang/Public/ShaderLang.h index edca638c..7b12ad07 100644 --- a/glslang/Public/ShaderLang.h +++ b/glslang/Public/ShaderLang.h @@ -249,6 +249,7 @@ SH_IMPORT_EXPORT int ShGetUniformLocation(const ShHandle uniformMap, const char* #include #include +#include class TCompiler; class TInfoSink; @@ -288,14 +289,34 @@ public: void setStringsWithLengthsAndNames( const char* const* s, const int* l, const char* const* names, int n); void setPreamble(const char* s) { preamble = s; } - bool parse(const TBuiltInResource*, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, bool forwardCompatible, EShMessages); + + // Interface to #include handlers. + class Includer { + public: + // On success, returns true and the content that replaces "#include + // filename". On failure, returns false and an arbitrary string. + virtual std::pair include(const char* filename) const = 0; + }; + + // Generates #error as #include content. + class ForbidInclude : public Includer { + public: + std::pair include(const char* filename) const override + { + return std::make_pair(true, "#error unexpected include directive\n"); + } + }; + + bool parse(const TBuiltInResource*, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, + bool forwardCompatible, EShMessages, const Includer& = ForbidInclude()); + // Equivalent to parse() without a default profile and without forcing defaults. // Provided for backwards compatibility. bool parse(const TBuiltInResource*, int defaultVersion, bool forwardCompatible, EShMessages); bool preprocess(const TBuiltInResource* builtInResources, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, - bool forwardCompatible, - EShMessages message, std::string* outputString); + bool forwardCompatible, EShMessages message, std::string* outputString, + const TShader::Includer& includer); const char* getInfoLog(); const char* getInfoDebugLog(); From 9c1280b225e5e9f0d1a35bce96e67c92bbb7330c Mon Sep 17 00:00:00 2001 From: Lei Zhang Date: Thu, 16 Jul 2015 17:33:10 -0400 Subject: [PATCH 2/5] Use extension framework to enable #include directive. This patch introduces a new extension, GL_GOOGLE_include_directive, to enable support #include directives. It depends on the extension GL_GOOGLE_cpp_style_line_directive. --- .../preprocessor.include.disabled.vert.err | 13 +++++++++++++ ...=> preprocessor.include.disabled.vert.out} | 0 ... => preprocessor.include.enabled.vert.err} | 0 .../preprocessor.include.enabled.vert.out | 0 ...ert => preprocessor.include.disabled.vert} | 13 +++++++------ Test/preprocessor.include.enabled.vert | 7 +++++++ Test/test-preprocessor-list | 3 ++- glslang/MachineIndependent/ParseHelper.h | 2 +- glslang/MachineIndependent/Versions.cpp | 19 ++++++++++++++++--- glslang/MachineIndependent/Versions.h | 2 ++ .../MachineIndependent/preprocessor/Pp.cpp | 5 +++-- 11 files changed, 51 insertions(+), 13 deletions(-) create mode 100644 Test/baseResults/preprocessor.include.disabled.vert.err rename Test/baseResults/{preprocessor.include.vert.out => preprocessor.include.disabled.vert.out} (100%) rename Test/baseResults/{preprocessor.include.vert.err => preprocessor.include.enabled.vert.err} (100%) create mode 100644 Test/baseResults/preprocessor.include.enabled.vert.out rename Test/{preprocessor.include.vert => preprocessor.include.disabled.vert} (76%) create mode 100644 Test/preprocessor.include.enabled.vert diff --git a/Test/baseResults/preprocessor.include.disabled.vert.err b/Test/baseResults/preprocessor.include.disabled.vert.err new file mode 100644 index 00000000..85428de8 --- /dev/null +++ b/Test/baseResults/preprocessor.include.disabled.vert.err @@ -0,0 +1,13 @@ +ERROR: 0:8000: '#include' : required extension not requested: GL_GOOGLE_include_directive +ERROR: 0:8000: '#include' : must be followed by a file designation +ERROR: 0:8001: '#include' : required extension not requested: GL_GOOGLE_include_directive +ERROR: 0:8001: '#include' : must be followed by a file designation +ERROR: 0:8002: '#include' : required extension not requested: GL_GOOGLE_include_directive +ERROR: 0:8002: '#error' : unexpected include directive +ERROR: 0:8003: '#include' : required extension not requested: GL_GOOGLE_include_directive +ERROR: 0:8003: '#include' : extra content after file designation +ERROR: 0:8004: '#include' : required extension not requested: GL_GOOGLE_include_directive +ERROR: 0:8004: '#error' : unexpected include directive +ERROR: 10 compilation errors. No code generated. + + diff --git a/Test/baseResults/preprocessor.include.vert.out b/Test/baseResults/preprocessor.include.disabled.vert.out similarity index 100% rename from Test/baseResults/preprocessor.include.vert.out rename to Test/baseResults/preprocessor.include.disabled.vert.out diff --git a/Test/baseResults/preprocessor.include.vert.err b/Test/baseResults/preprocessor.include.enabled.vert.err similarity index 100% rename from Test/baseResults/preprocessor.include.vert.err rename to Test/baseResults/preprocessor.include.enabled.vert.err diff --git a/Test/baseResults/preprocessor.include.enabled.vert.out b/Test/baseResults/preprocessor.include.enabled.vert.out new file mode 100644 index 00000000..e69de29b diff --git a/Test/preprocessor.include.vert b/Test/preprocessor.include.disabled.vert similarity index 76% rename from Test/preprocessor.include.vert rename to Test/preprocessor.include.disabled.vert index 512327ba..130d928e 100644 --- a/Test/preprocessor.include.vert +++ b/Test/preprocessor.include.disabled.vert @@ -1,6 +1,7 @@ -#line 8000 -#include -#include 123 -#include "foo" -#include "foo" garbage -#include "no-eol" \ No newline at end of file +#line 8000 +#include +#include 123 +#include "foo" +#include "foo" garbage +#include "no-eol" + diff --git a/Test/preprocessor.include.enabled.vert b/Test/preprocessor.include.enabled.vert new file mode 100644 index 00000000..ecdf466e --- /dev/null +++ b/Test/preprocessor.include.enabled.vert @@ -0,0 +1,7 @@ +#extension GL_GOOGLE_include_directive : enable +#line 8000 +#include +#include 123 +#include "foo" +#include "foo" garbage +#include "no-eol" diff --git a/Test/test-preprocessor-list b/Test/test-preprocessor-list index 195b4a79..cd2381a5 100644 --- a/Test/test-preprocessor-list +++ b/Test/test-preprocessor-list @@ -4,7 +4,8 @@ preprocessor.edge_cases.vert preprocessor.errors.vert preprocessor.extensions.vert preprocessor.function_macro.vert -preprocessor.include.vert +preprocessor.include.enabled.vert +preprocessor.include.disabled.vert preprocessor.line.vert preprocessor.line.frag preprocessor.pragma.vert diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h index f0c63bb3..e537ec62 100644 --- a/glslang/MachineIndependent/ParseHelper.h +++ b/glslang/MachineIndependent/ParseHelper.h @@ -235,7 +235,7 @@ public: void requireStage(const TSourceLoc&, EShLanguage, const char* featureDesc); void checkDeprecated(const TSourceLoc&, int queryProfiles, int depVersion, const char* featureDesc); void requireNotRemoved(const TSourceLoc&, int queryProfiles, int removedVersion, const char* featureDesc); - void requireExtensions(const TSourceLoc&, int numExtensions, const char* const extensions[], const char* featureDesc); + void requireExtensions(const TSourceLoc&, int numExtensions, const char* const extensions[], const char* featureDesc, bool requiredByPreprocessor = false); TExtensionBehavior getExtensionBehavior(const char*); bool extensionTurnedOn(const char* const extension); bool extensionsTurnedOn(int numExtensions, const char* const extensions[]); diff --git a/glslang/MachineIndependent/Versions.cpp b/glslang/MachineIndependent/Versions.cpp index 28ad10cb..46275b6c 100644 --- a/glslang/MachineIndependent/Versions.cpp +++ b/glslang/MachineIndependent/Versions.cpp @@ -174,7 +174,9 @@ void TParseContext::initializeExtensionBehavior() extensionBehavior[E_GL_ARB_viewport_array] = EBhDisable; // extensionBehavior[E_GL_ARB_cull_distance] = EBhDisable; // present for 4.5, but need extension control over block members + // #line and #include extensionBehavior[E_GL_GOOGLE_cpp_style_line_directive] = EBhDisable; + extensionBehavior[E_GL_GOOGLE_include_directive] = EBhDisable; // AEP extensionBehavior[E_GL_ANDROID_extension_pack_es31a] = EBhDisablePartial; @@ -219,7 +221,9 @@ const char* TParseContext::getPreamble() "#define GL_OES_EGL_image_external 1\n" "#define GL_EXT_shader_texture_lod 1\n" + // #line and #include "#define GL_GOOGLE_cpp_style_line_directive 1\n" + "#define GL_GOOGLE_include_directive 1\n" // AEP "#define GL_ANDROID_extension_pack_es31a 1\n" @@ -270,6 +274,7 @@ const char* TParseContext::getPreamble() "#define GL_ARB_viewport_array 1\n" "#define GL_GOOGLE_cpp_style_line_directive 1\n" + "#define GL_GOOGLE_include_directive 1\n" // "#define GL_ARB_cull_distance 1\n" // present for 4.5, but need extension control over block members ; } @@ -410,7 +415,7 @@ void TParseContext::requireNotRemoved(const TSourceLoc& loc, int profileMask, in // Use when there are no profile/version to check, it's just an error if one of the // extensions is not present. // -void TParseContext::requireExtensions(const TSourceLoc& loc, int numExtensions, const char* const extensions[], const char* featureDesc) +void TParseContext::requireExtensions(const TSourceLoc& loc, int numExtensions, const char* const extensions[], const char* featureDesc, bool requiredByPreprocessor) { // First, see if any of the extensions are enabled for (int i = 0; i < numExtensions; ++i) { @@ -437,9 +442,15 @@ void TParseContext::requireExtensions(const TSourceLoc& loc, int numExtensions, // If we get this far, give errors explaining what extensions are needed if (numExtensions == 1) - error(loc, "required extension not requested:", featureDesc, extensions[0]); + if (requiredByPreprocessor) + ppError(loc, "required extension not requested:", featureDesc, extensions[0]); + else + error(loc, "required extension not requested:", featureDesc, extensions[0]); else { - error(loc, "required extension not requested:", featureDesc, "Possible extensions include:"); + if (requiredByPreprocessor) + ppError(loc, "required extension not requested:", featureDesc, "Possible extensions include:"); + else + error(loc, "required extension not requested:", featureDesc, "Possible extensions include:"); for (int i = 0; i < numExtensions; ++i) infoSink.info.message(EPrefixNone, extensions[i]); } @@ -525,6 +536,8 @@ void TParseContext::updateExtensionBehavior(int line, const char* extension, con updateExtensionBehavior(line, "GL_EXT_shader_io_blocks", behaviorString); else if (strcmp(extension, "GL_OES_tessellation_shader") == 0) updateExtensionBehavior(line, "GL_OES_shader_io_blocks", behaviorString); + else if (strcmp(extension, "GL_GOOGLE_include_directive") == 0) + updateExtensionBehavior(line, "GL_GOOGLE_cpp_style_line_directive", behaviorString); } void TParseContext::updateExtensionBehavior(const char* extension, TExtensionBehavior behavior) diff --git a/glslang/MachineIndependent/Versions.h b/glslang/MachineIndependent/Versions.h index ae8fd414..bb3cbc4d 100644 --- a/glslang/MachineIndependent/Versions.h +++ b/glslang/MachineIndependent/Versions.h @@ -112,7 +112,9 @@ const char* const E_GL_ARB_shader_texture_image_samples = "GL_ARB_shader_texture const char* const E_GL_ARB_viewport_array = "GL_ARB_viewport_array"; //const char* const E_GL_ARB_cull_distance = "GL_ARB_cull_distance"; // present for 4.5, but need extension control over block members +// #line and #include const char* const E_GL_GOOGLE_cpp_style_line_directive = "GL_GOOGLE_cpp_style_line_directive"; +const char* const E_GL_GOOGLE_include_directive = "GL_GOOGLE_include_directive"; // AEP const char* const E_GL_ANDROID_extension_pack_es31a = "GL_ANDROID_extension_pack_es31a"; diff --git a/glslang/MachineIndependent/preprocessor/Pp.cpp b/glslang/MachineIndependent/preprocessor/Pp.cpp index 9e126824..c17004b6 100644 --- a/glslang/MachineIndependent/preprocessor/Pp.cpp +++ b/glslang/MachineIndependent/preprocessor/Pp.cpp @@ -661,7 +661,7 @@ int TPpContext::CPPline(TPpToken* ppToken) if (token != '\n') { if (token == PpAtomConstString) { - parseContext.requireExtensions(directiveLoc, 1, &E_GL_GOOGLE_cpp_style_line_directive, "filename-based #line"); + parseContext.requireExtensions(directiveLoc, 1, &E_GL_GOOGLE_cpp_style_line_directive, "filename-based #line", true); // We need to save a copy of the string instead of pointing // to the name field of the token since the name field // will likely be overwritten by the next token scan. @@ -878,6 +878,7 @@ int TPpContext::readCPPline(TPpToken* ppToken) token = CPPifdef(0, ppToken); break; case PpAtomInclude: + parseContext.requireExtensions(ppToken->loc, 1, &E_GL_GOOGLE_include_directive, "#include", true); token = CPPinclude(ppToken); break; case PpAtomLine: @@ -1002,7 +1003,7 @@ int TPpContext::MacroExpand(int atom, TPpToken* ppToken, bool expandUndef, bool case PpAtomFileMacro: { if (const char* current_file = parseContext.getCurrentLoc().name) { - parseContext.requireExtensions(ppToken->loc, 1, &E_GL_GOOGLE_cpp_style_line_directive, "filename-based __FILE__"); + parseContext.requireExtensions(ppToken->loc, 1, &E_GL_GOOGLE_cpp_style_line_directive, "filename-based __FILE__", true); sprintf(ppToken->name, "\"%s\"", current_file); } else { ppToken->ival = parseContext.getCurrentLoc().string; From 2b4ebbb1e1882821f0c552776e4110649b0ddc25 Mon Sep 17 00:00:00 2001 From: Lei Zhang Date: Sun, 5 Jul 2015 16:50:21 -0400 Subject: [PATCH 3/5] Update scanner in TParseContext when changing the input stream. After parsing a #include directive, we push a TokenizableString which contains the content of the included file into the input stack. Henceforth, tokens will be read from the newly pushed TokenizableString. However, the scanner in TParseContext still points to the previous input stream. We need to update the scanner to point to the new input stream inside TokenizableString. Thus, the setCurrent{String|Line|..} method in TParseContext updates the status of the correct input stream. After finishing the newly pushed TokenizableString, we need to restore the scanner to the previous input stream. --- glslang/MachineIndependent/ParseHelper.h | 1 + .../MachineIndependent/preprocessor/Pp.cpp | 2 +- .../preprocessor/PpContext.h | 25 +++++++++++++++++-- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h index e537ec62..83d0d563 100644 --- a/glslang/MachineIndependent/ParseHelper.h +++ b/glslang/MachineIndependent/ParseHelper.h @@ -218,6 +218,7 @@ public: void setCurrentSourceName(const char* name) { currentScanner->setFile(name); } void setCurrentString(int string) { currentScanner->setString(string); } void setScanner(TInputScanner* scanner) { currentScanner = scanner; } + TInputScanner* getScanner() const { return currentScanner; } bool lineDirectiveShouldSetNextLine() const; diff --git a/glslang/MachineIndependent/preprocessor/Pp.cpp b/glslang/MachineIndependent/preprocessor/Pp.cpp index c17004b6..8e58a8c2 100644 --- a/glslang/MachineIndependent/preprocessor/Pp.cpp +++ b/glslang/MachineIndependent/preprocessor/Pp.cpp @@ -615,7 +615,7 @@ int TPpContext::CPPinclude(TPpToken* ppToken) bool success; std::tie(success, replacement) = includer.include(name); if (success) { - pushInput(new TokenizableString(replacement, this)); + pushInput(new TokenizableString(directiveLoc, replacement, this)); // At EOF, there's no "current" location anymore. if (token != EndOfInput) parseContext.setCurrentColumn(0); // Don't accidentally return EndOfInput, which will end all preprocessing. diff --git a/glslang/MachineIndependent/preprocessor/PpContext.h b/glslang/MachineIndependent/preprocessor/PpContext.h index 82564ebc..5d06e701 100644 --- a/glslang/MachineIndependent/preprocessor/PpContext.h +++ b/glslang/MachineIndependent/preprocessor/PpContext.h @@ -134,6 +134,10 @@ public: virtual int getch() = 0; virtual void ungetch() = 0; + // Will be called when we start reading tokens from this instance + virtual void notifyActivated() {} + // Will be called when we do not read tokens from this instance anymore + virtual void notifyDeleted() {} protected: bool done; TPpContext* pp; @@ -144,9 +148,11 @@ public: void pushInput(tInput* in) { inputStack.push_back(in); + in->notifyActivated(); } void popInput() { + inputStack.back()->notifyDeleted(); delete inputStack.back(); inputStack.pop_back(); } @@ -426,19 +432,31 @@ protected: class TokenizableString : public tInput { public: // Copies str, which must be non-empty. - TokenizableString(const std::string& str, TPpContext* pp) + TokenizableString(const TSourceLoc& startLoc, const std::string& str, TPpContext* pp) : tInput(pp), str_(str), strings(str_.data()), length(str_.size()), scanner(1, &strings, &length), - stringInput(pp, scanner) {} + prevScanner(nullptr), + stringInput(pp, scanner) { + scanner.setLine(startLoc.line); + scanner.setString(startLoc.string); + scanner.setFile(startLoc.name); + } // tInput methods: int scan(TPpToken* t) override { return stringInput.scan(t); } int getch() override { return stringInput.getch(); } void ungetch() override { stringInput.ungetch(); } + void notifyActivated() override + { + prevScanner = pp->parseContext.getScanner(); + pp->parseContext.setScanner(&scanner); + } + void notifyDeleted() override { pp->parseContext.setScanner(prevScanner); } + private: // Stores the titular string. const std::string str_; @@ -448,6 +466,9 @@ protected: size_t length; // Scans over str_. TInputScanner scanner; + // The previous effective scanner before the scanner in this instance + // has been activated. + TInputScanner* prevScanner; // Delegate object implementing the tInput interface. tStringInput stringInput; }; From 1eed969b2db28e09e3fae163a456800a0660e7ce Mon Sep 17 00:00:00 2001 From: Lei Zhang Date: Mon, 6 Jul 2015 10:32:46 -0400 Subject: [PATCH 4/5] Output wrapping #line directives for included content. Also changed the includer interface to let it return the actual full path of the included file. --- .../preprocessor.include.disabled.vert.err | 4 +-- .../preprocessor.include.enabled.vert.err | 10 +++---- .../MachineIndependent/preprocessor/Pp.cpp | 28 ++++++++++++++----- glslang/Public/ShaderLang.h | 13 +++++---- 4 files changed, 35 insertions(+), 20 deletions(-) diff --git a/Test/baseResults/preprocessor.include.disabled.vert.err b/Test/baseResults/preprocessor.include.disabled.vert.err index 85428de8..1145a314 100644 --- a/Test/baseResults/preprocessor.include.disabled.vert.err +++ b/Test/baseResults/preprocessor.include.disabled.vert.err @@ -3,11 +3,11 @@ ERROR: 0:8000: '#include' : must be followed by a file designation ERROR: 0:8001: '#include' : required extension not requested: GL_GOOGLE_include_directive ERROR: 0:8001: '#include' : must be followed by a file designation ERROR: 0:8002: '#include' : required extension not requested: GL_GOOGLE_include_directive -ERROR: 0:8002: '#error' : unexpected include directive +ERROR: 0:8002: '#include' : unexpected include directive ERROR: 0:8003: '#include' : required extension not requested: GL_GOOGLE_include_directive ERROR: 0:8003: '#include' : extra content after file designation ERROR: 0:8004: '#include' : required extension not requested: GL_GOOGLE_include_directive -ERROR: 0:8004: '#error' : unexpected include directive +ERROR: 0:8004: '#include' : unexpected include directive ERROR: 10 compilation errors. No code generated. diff --git a/Test/baseResults/preprocessor.include.enabled.vert.err b/Test/baseResults/preprocessor.include.enabled.vert.err index edc9ba9d..2cdf3f28 100644 --- a/Test/baseResults/preprocessor.include.enabled.vert.err +++ b/Test/baseResults/preprocessor.include.enabled.vert.err @@ -1,8 +1,8 @@ -ERROR: 0:8000: '#include' : must be followed by a file designation -ERROR: 0:8001: '#include' : must be followed by a file designation -ERROR: 0:8002: '#error' : unexpected include directive -ERROR: 0:8003: '#include' : extra content after file designation -ERROR: 0:8004: '#error' : unexpected include directive +ERROR: 0:8000: '#include' : must be followed by a file designation +ERROR: 0:8001: '#include' : must be followed by a file designation +ERROR: 0:8002: '#include' : unexpected include directive +ERROR: 0:8003: '#include' : extra content after file designation +ERROR: 0:8004: '#include' : unexpected include directive ERROR: 5 compilation errors. No code generated. diff --git a/glslang/MachineIndependent/preprocessor/Pp.cpp b/glslang/MachineIndependent/preprocessor/Pp.cpp index 8e58a8c2..2ec7e74c 100644 --- a/glslang/MachineIndependent/preprocessor/Pp.cpp +++ b/glslang/MachineIndependent/preprocessor/Pp.cpp @@ -83,6 +83,7 @@ NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include #include #include #include @@ -605,23 +606,36 @@ int TPpContext::CPPinclude(TPpToken* ppToken) // TODO: handle angle brackets. parseContext.ppError(directiveLoc, "must be followed by a file designation", "#include", ""); } else { - const char* name = GetAtomString(ppToken->atom); + // Make a copy of the name because it will be overwritten by the next token scan. + const std::string filename = ppToken->name; token = scanToken(ppToken); if (token != '\n' && token != EndOfInput) { parseContext.ppError(ppToken->loc, "extra content after file designation", "#include", ""); } else { - if (!inputStack.empty()) ungetChar(); + std::string sourceName; std::string replacement; - bool success; - std::tie(success, replacement) = includer.include(name); - if (success) { - pushInput(new TokenizableString(directiveLoc, replacement, this)); + std::tie(sourceName, replacement) = includer.include(filename.c_str()); + if (!sourceName.empty()) { + if (!replacement.empty()) { + const bool forNextLine = parseContext.lineDirectiveShouldSetNextLine(); + std::ostringstream content; + content << "#line " << forNextLine << " " << "\"" << sourceName << "\"\n"; + content << replacement << (replacement.back() == '\n' ? "" : "\n"); + content << "#line " << directiveLoc.line + forNextLine << " "; + if (directiveLoc.name != nullptr) { + content << "\"" << directiveLoc.name << "\""; + } else { + content << directiveLoc.string; + } + content << "\n"; + pushInput(new TokenizableString(directiveLoc, content.str(), this)); + } // At EOF, there's no "current" location anymore. if (token != EndOfInput) parseContext.setCurrentColumn(0); // Don't accidentally return EndOfInput, which will end all preprocessing. return '\n'; } else { - parseContext.ppError(ppToken->loc, "not found", name, ""); + parseContext.ppError(directiveLoc, replacement.c_str(), "#include", ""); } } } diff --git a/glslang/Public/ShaderLang.h b/glslang/Public/ShaderLang.h index 7b12ad07..3fbfe354 100644 --- a/glslang/Public/ShaderLang.h +++ b/glslang/Public/ShaderLang.h @@ -293,17 +293,18 @@ public: // Interface to #include handlers. class Includer { public: - // On success, returns true and the content that replaces "#include - // filename". On failure, returns false and an arbitrary string. - virtual std::pair include(const char* filename) const = 0; + // On success, returns the full path and content of the file with the given + // filename that replaces "#include filename". On failure, returns an empty + // string and an error message. + virtual std::pair include(const char* filename) const = 0; }; - // Generates #error as #include content. + // Returns an error message for any #include directive. class ForbidInclude : public Includer { public: - std::pair include(const char* filename) const override + std::pair include(const char* filename) const override { - return std::make_pair(true, "#error unexpected include directive\n"); + return std::make_pair("", "unexpected include directive"); } }; From 484bb127030b2132ae98f4cd73b933fb4230e128 Mon Sep 17 00:00:00 2001 From: Lei Zhang Date: Wed, 5 Aug 2015 11:56:14 -0400 Subject: [PATCH 5/5] Create a new ppRequireExtensions method for preprocessor. Now extensions required by preprocessor should be checked via the ppRequireExtensions method. This is more clear and coherent with the rest of the code. --- glslang/MachineIndependent/ParseHelper.h | 6 ++- glslang/MachineIndependent/Versions.cpp | 50 +++++++++++++------ .../MachineIndependent/preprocessor/Pp.cpp | 6 +-- 3 files changed, 42 insertions(+), 20 deletions(-) diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h index 83d0d563..e008c0f2 100644 --- a/glslang/MachineIndependent/ParseHelper.h +++ b/glslang/MachineIndependent/ParseHelper.h @@ -236,7 +236,8 @@ public: void requireStage(const TSourceLoc&, EShLanguage, const char* featureDesc); void checkDeprecated(const TSourceLoc&, int queryProfiles, int depVersion, const char* featureDesc); void requireNotRemoved(const TSourceLoc&, int queryProfiles, int removedVersion, const char* featureDesc); - void requireExtensions(const TSourceLoc&, int numExtensions, const char* const extensions[], const char* featureDesc, bool requiredByPreprocessor = false); + void requireExtensions(const TSourceLoc&, int numExtensions, const char* const extensions[], const char* featureDesc); + void ppRequireExtensions(const TSourceLoc&, int numExtensions, const char* const extensions[], const char* featureDesc); TExtensionBehavior getExtensionBehavior(const char*); bool extensionTurnedOn(const char* const extension); bool extensionsTurnedOn(int numExtensions, const char* const extensions[]); @@ -252,13 +253,14 @@ public: protected: void nonInitConstCheck(const TSourceLoc&, TString& identifier, TType& type); - void inheritGlobalDefaults(TQualifier& dst) const; + void inheritGlobalDefaults(TQualifier& dst) const; TVariable* makeInternalVariable(const char* name, const TType&) const; TVariable* declareNonArray(const TSourceLoc&, TString& identifier, TType&, bool& newDeclaration); void declareArray(const TSourceLoc&, TString& identifier, const TType&, TSymbol*&, bool& newDeclaration); TIntermNode* executeInitializer(const TSourceLoc&, TIntermTyped* initializer, TVariable* variable); TIntermTyped* convertInitializerList(const TSourceLoc&, const TType&, TIntermTyped* initializer); TOperator mapTypeToConstructorOp(const TType&) const; + bool checkExtensionsRequested(const TSourceLoc&, int numExtensions, const char* const extensions[], const char* featureDesc); void updateExtensionBehavior(const char* const extension, TExtensionBehavior); void finalErrorCheck(); void outputMessage(const TSourceLoc&, const char* szReason, const char* szToken, diff --git a/glslang/MachineIndependent/Versions.cpp b/glslang/MachineIndependent/Versions.cpp index 46275b6c..c751076f 100644 --- a/glslang/MachineIndependent/Versions.cpp +++ b/glslang/MachineIndependent/Versions.cpp @@ -411,17 +411,15 @@ void TParseContext::requireNotRemoved(const TSourceLoc& loc, int profileMask, in } } -// -// Use when there are no profile/version to check, it's just an error if one of the -// extensions is not present. -// -void TParseContext::requireExtensions(const TSourceLoc& loc, int numExtensions, const char* const extensions[], const char* featureDesc, bool requiredByPreprocessor) +// Returns true if at least one of the extensions in the extensions parameter is requested. Otherwise, returns false. +// Warns appropriately if the requested behavior of an extension is "warn". +bool TParseContext::checkExtensionsRequested(const TSourceLoc& loc, int numExtensions, const char* const extensions[], const char* featureDesc) { // First, see if any of the extensions are enabled for (int i = 0; i < numExtensions; ++i) { TExtensionBehavior behavior = getExtensionBehavior(extensions[i]); if (behavior == EBhEnable || behavior == EBhRequire) - return; + return true; } // See if any extensions want to give a warning on use; give warnings for all such extensions @@ -438,19 +436,41 @@ void TParseContext::requireExtensions(const TSourceLoc& loc, int numExtensions, } } if (warned) - return; + return true; + return false; +} + +// +// Use when there are no profile/version to check, it's just an error if one of the +// extensions is not present. +// +void TParseContext::requireExtensions(const TSourceLoc& loc, int numExtensions, const char* const extensions[], const char* featureDesc) +{ + if (checkExtensionsRequested(loc, numExtensions, extensions, featureDesc)) return; // If we get this far, give errors explaining what extensions are needed if (numExtensions == 1) - if (requiredByPreprocessor) - ppError(loc, "required extension not requested:", featureDesc, extensions[0]); - else - error(loc, "required extension not requested:", featureDesc, extensions[0]); + error(loc, "required extension not requested:", featureDesc, extensions[0]); else { - if (requiredByPreprocessor) - ppError(loc, "required extension not requested:", featureDesc, "Possible extensions include:"); - else - error(loc, "required extension not requested:", featureDesc, "Possible extensions include:"); + error(loc, "required extension not requested:", featureDesc, "Possible extensions include:"); + for (int i = 0; i < numExtensions; ++i) + infoSink.info.message(EPrefixNone, extensions[i]); + } +} + +// +// Use by preprocessor when there are no profile/version to check, it's just an error if one of the +// extensions is not present. +// +void TParseContext::ppRequireExtensions(const TSourceLoc& loc, int numExtensions, const char* const extensions[], const char* featureDesc) +{ + if (checkExtensionsRequested(loc, numExtensions, extensions, featureDesc)) return; + + // If we get this far, give errors explaining what extensions are needed + if (numExtensions == 1) + ppError(loc, "required extension not requested:", featureDesc, extensions[0]); + else { + ppError(loc, "required extension not requested:", featureDesc, "Possible extensions include:"); for (int i = 0; i < numExtensions; ++i) infoSink.info.message(EPrefixNone, extensions[i]); } diff --git a/glslang/MachineIndependent/preprocessor/Pp.cpp b/glslang/MachineIndependent/preprocessor/Pp.cpp index 2ec7e74c..d62b7369 100644 --- a/glslang/MachineIndependent/preprocessor/Pp.cpp +++ b/glslang/MachineIndependent/preprocessor/Pp.cpp @@ -675,7 +675,7 @@ int TPpContext::CPPline(TPpToken* ppToken) if (token != '\n') { if (token == PpAtomConstString) { - parseContext.requireExtensions(directiveLoc, 1, &E_GL_GOOGLE_cpp_style_line_directive, "filename-based #line", true); + parseContext.ppRequireExtensions(directiveLoc, 1, &E_GL_GOOGLE_cpp_style_line_directive, "filename-based #line"); // We need to save a copy of the string instead of pointing // to the name field of the token since the name field // will likely be overwritten by the next token scan. @@ -892,7 +892,7 @@ int TPpContext::readCPPline(TPpToken* ppToken) token = CPPifdef(0, ppToken); break; case PpAtomInclude: - parseContext.requireExtensions(ppToken->loc, 1, &E_GL_GOOGLE_include_directive, "#include", true); + parseContext.ppRequireExtensions(ppToken->loc, 1, &E_GL_GOOGLE_include_directive, "#include"); token = CPPinclude(ppToken); break; case PpAtomLine: @@ -1017,7 +1017,7 @@ int TPpContext::MacroExpand(int atom, TPpToken* ppToken, bool expandUndef, bool case PpAtomFileMacro: { if (const char* current_file = parseContext.getCurrentLoc().name) { - parseContext.requireExtensions(ppToken->loc, 1, &E_GL_GOOGLE_cpp_style_line_directive, "filename-based __FILE__", true); + parseContext.ppRequireExtensions(ppToken->loc, 1, &E_GL_GOOGLE_cpp_style_line_directive, "filename-based __FILE__"); sprintf(ppToken->name, "\"%s\"", current_file); } else { ppToken->ival = parseContext.getCurrentLoc().string;