From 958f162fba8976ae9b5473eb14c7978e8cac7687 Mon Sep 17 00:00:00 2001 From: Luca Fascione Date: Thu, 17 Jul 2025 15:18:50 +0200 Subject: [PATCH] [oslcomp] Reworked source location to include column numbers and track begin/end pairs. Introduce OSL::pvt::SrcLoc to track this and percolate throughout the source Signed-off-by: Luca Fascione --- src/include/osl_pvt.h | 123 ++++++++++++++++-- src/liboslcomp/ast.cpp | 62 +++++----- src/liboslcomp/ast.h | 17 +-- src/liboslcomp/codegen.cpp | 5 +- src/liboslcomp/oslcomp.cpp | 192 +++++++++++++++++------------ src/liboslcomp/oslcomp_pvt.h | 159 +++++++++++++++++------- src/liboslcomp/oslgram.y | 156 +++++++++++------------ src/liboslcomp/osllex.l | 162 ++++++++++++------------ src/liboslcomp/typecheck.cpp | 5 +- src/liboslexec/loadshader.cpp | 32 +++-- src/liboslexec/runtimeoptimize.cpp | 6 +- 11 files changed, 553 insertions(+), 366 deletions(-) diff --git a/src/include/osl_pvt.h b/src/include/osl_pvt.h index f36e3d280..09c4ea691 100644 --- a/src/include/osl_pvt.h +++ b/src/include/osl_pvt.h @@ -15,6 +15,111 @@ namespace pvt { class ASTNode; class StructSpec; +/// A location in the OSL source +/// Conversions from the FLEX/Bison convention happens in from_yyloc() +/// It's tempting to make this class also track the straight byte-offset +/// in the file, which would help us when we print out errors, code in the +/// OSO's and whathaveyou. But the action of the preprocessor kills our +/// ability to do so, as the parser doesn't ever see the user's version +/// of the source +struct SrcLoc { + static const uint32_t kUnknown { uint32_t(-1) }; + + ustring filename; + // this storage asymmetry is weird, but it's what you want: + // the column markers work like a normal STL [begin, end) span, + // but the line markers are actually [start, stop] because they + // need to say on which line the non-inclusive `column_end` side is + uint32_t line_start = kUnknown; ///< start line number, 0-based, inclusive + uint32_t column_begin + = kUnknown; ///< start col number, 0-based, [begin, end)-style like the STL + uint32_t line_stop = kUnknown; ///< finish line number, 0-based, inclusive + uint32_t column_end + = kUnknown; ///< finish col number, 0-based, [begin, end)-style like the STL + + SrcLoc() = default; + + SrcLoc(ustring filename, int first_line, int first_column, int last_line, int last_column) : + filename(filename) + { + from_yyloc(first_line, first_column, last_line, last_column); + } + + explicit operator bool() const { return !filename.empty(); } + + /// Convert the classic/built-in representation of a YYLTYPE + /// to our convention + void from_yyloc(int first_line, int first_column, int last_line, + int last_column) + { + // we go from 1-based to 0-based + line_start = first_line ? first_line - 1 : kUnknown; + column_begin = first_column ? first_column - 1 : kUnknown; + line_stop = last_line ? last_line - 1 : kUnknown; + column_end = last_column ? last_column - 1 : kUnknown; + } + + bool operator==(const SrcLoc& other) const + { + return line_start == other.line_start + && column_begin == other.column_begin + && line_stop == other.line_stop + && column_end == other.column_end; + } + + bool operator!=(const SrcLoc& other) const { return !(*this == other); } + + /// Operator < compares the start locations only. + /// This is what you want, trust me + bool operator<(const SrcLoc& other) const + { + return line_start < other.line_start + || (line_start == other.line_start + && column_begin < other.column_begin); + } + + /// How many lines spanned by this token. + /// Unlike colcount(), which needs care to be used, this is always correct. + /// Also, unlike colcount(), this is probably nowhere near as useful + size_t linecount() const { return line_stop - line_start + 1; } + + /// How many columns spanned by this token. + /// N.B. This needs to be used with care: first off it is only ever a correct + /// notion when first_lineno() == last_lineno(), because we don't know how + /// long lines are. Further, the parser counts all whitespace as _one_ character + /// so '\t' counts as one, not 8 (or 4, or whatever your terminal is set to) + size_t colcount() const { return column_end - column_begin; } + + /// Line number, 1-based, for humans, first line of the location span, inclusive + int first_lineno() const { return line_start + 1; } + + /// Column number, 1-based, for humans, first line of the location span, inclusive + int first_colno() const { return column_begin + 1; } + + /// Line number, 1-based, for humans, last line of the location span, inclusive + int last_lineno() const { return line_stop + 1; } + + /// Column number, 1-based, for humans, last character of the location span, inclusive + // (because of our internal representation this does not need a "+1") + int last_colno() const { return column_end; } + + // see also fmt::formatter at the end of the file + friend std::ostream& operator<<(std::ostream& out, const SrcLoc& sl) + { + if (sl.column_begin != kUnknown) + return out << fmtformat("{}:{}:{}", sl.filename, sl.last_lineno(), + sl.last_colno()); + else if (sl.line_start != kUnknown) + return out << fmtformat("{}:{}", sl.filename, sl.last_lineno()); + else if (!sl.filename.empty()) + return out << fmtformat("{}", sl.filename); + + // if there is no filename we print nothing + return out; + } +}; + + /// Kinds of shaders /// @@ -937,7 +1042,7 @@ typedef std::vector SymbolPtrVec; class Opcode { public: Opcode(ustring op, ustring method, size_t firstarg = 0, size_t nargs = 0) - : m_firstarg((int)firstarg), m_method(method), m_sourceline(0) + : m_firstarg((int)firstarg), m_method(method) { reset(op, nargs); // does most of the heavy lifting } @@ -959,13 +1064,11 @@ class Opcode { int nargs() const { return m_nargs; } ustring method() const { return m_method; } void method(ustring method) { m_method = method; } - void source(ustring sourcefile, int sourceline) - { - m_sourcefile = sourcefile; - m_sourceline = sourceline; - } - ustring sourcefile() const { return m_sourcefile; } - int sourceline() const { return m_sourceline; } + void source(const SrcLoc& srcloc) { m_srcloc = srcloc; } + ustring sourcefile() const { return m_srcloc.filename; } + const SrcLoc& srcloc() const { return m_srcloc; } + // removeme + int sourceline() const { return m_srcloc.first_lineno(); } void set_args(size_t firstarg, size_t nargs) { @@ -1131,8 +1234,7 @@ class Opcode { int m_nargs; ///< Total number of arguments ustring m_method; ///< Which param or method this code is for int m_jump[max_jumps]; ///< Jump addresses (-1 means none) - ustring m_sourcefile; ///< Source filename for this op - int m_sourceline; ///< Line of source code for this op + SrcLoc m_srcloc; ///< Location in source code for this op unsigned int m_argread; ///< Bit field - which args are read unsigned int m_argwrite; ///< Bit field - which args are written unsigned int m_argtakesderivs; ///< Bit field - which args take derivs @@ -1172,5 +1274,6 @@ OSL_NAMESPACE_END #if FMT_VERSION >= 100000 FMT_BEGIN_NAMESPACE template<> struct formatter : ostream_formatter {}; +template<> struct formatter : ostream_formatter {}; FMT_END_NAMESPACE #endif diff --git a/src/liboslcomp/ast.cpp b/src/liboslcomp/ast.cpp index 6d0e4a858..4ff4f2fd7 100644 --- a/src/liboslcomp/ast.cpp +++ b/src/liboslcomp/ast.cpp @@ -71,8 +71,7 @@ reverse(ASTNode::ref list) ASTNode::ASTNode(NodeType nodetype, OSLCompilerImpl* compiler) : m_nodetype(nodetype) , m_compiler(compiler) - , m_sourcefile(compiler->filename()) - , m_sourceline(compiler->lineno()) + , m_srcloc(compiler->srcloc()) , m_op(0) , m_is_lvalue(false) { @@ -88,8 +87,7 @@ ASTNode::ASTNode(NodeType nodetype, OSLCompilerImpl* compiler, int op, ASTNode* a) : m_nodetype(nodetype) , m_compiler(compiler) - , m_sourcefile(compiler->filename()) - , m_sourceline(compiler->lineno()) + , m_srcloc(compiler->srcloc()) , m_op(op) , m_is_lvalue(false) { @@ -105,8 +103,7 @@ ASTNode::ASTNode(NodeType nodetype, OSLCompilerImpl* compiler, int op, ASTNode::ASTNode(NodeType nodetype, OSLCompilerImpl* compiler, int op) : m_nodetype(nodetype) , m_compiler(compiler) - , m_sourcefile(compiler->filename()) - , m_sourceline(compiler->lineno()) + , m_srcloc(compiler->srcloc()) , m_op(op) , m_is_lvalue(false) { @@ -122,8 +119,7 @@ ASTNode::ASTNode(NodeType nodetype, OSLCompilerImpl* compiler, int op, ASTNode* a, ASTNode* b) : m_nodetype(nodetype) , m_compiler(compiler) - , m_sourcefile(compiler->filename()) - , m_sourceline(compiler->lineno()) + , m_srcloc(compiler->srcloc()) , m_op(op) , m_is_lvalue(false) { @@ -141,8 +137,7 @@ ASTNode::ASTNode(NodeType nodetype, OSLCompilerImpl* compiler, int op, ASTNode* a, ASTNode* b, ASTNode* c) : m_nodetype(nodetype) , m_compiler(compiler) - , m_sourcefile(compiler->filename()) - , m_sourceline(compiler->lineno()) + , m_srcloc(compiler->srcloc()) , m_op(op) , m_is_lvalue(false) { @@ -161,8 +156,7 @@ ASTNode::ASTNode(NodeType nodetype, OSLCompilerImpl* compiler, int op, ASTNode* a, ASTNode* b, ASTNode* c, ASTNode* d) : m_nodetype(nodetype) , m_compiler(compiler) - , m_sourcefile(compiler->filename()) - , m_sourceline(compiler->lineno()) + , m_srcloc(compiler->srcloc()) , m_op(op) , m_is_lvalue(false) { @@ -213,7 +207,7 @@ ASTNode::~ASTNode() void ASTNode::error_impl(string_view msg) const { - m_compiler->errorfmt(sourcefile(), sourceline(), "{}", msg); + m_compiler->errorfmt(sourceloc(), "{}", msg); } @@ -221,7 +215,7 @@ ASTNode::error_impl(string_view msg) const void ASTNode::warning_impl(string_view msg) const { - m_compiler->warningfmt(sourcefile(), sourceline(), "{}", msg); + m_compiler->warningfmt(sourceloc(), "{}", msg); } @@ -229,7 +223,7 @@ ASTNode::warning_impl(string_view msg) const void ASTNode::info_impl(string_view msg) const { - m_compiler->infofmt(sourcefile(), sourceline(), "{}", msg); + m_compiler->infofmt(sourceloc(), "{}", msg); } @@ -237,7 +231,7 @@ ASTNode::info_impl(string_view msg) const void ASTNode::message_impl(string_view msg) const { - m_compiler->messagefmt(sourcefile(), sourceline(), "{}", msg); + m_compiler->messagefmt(sourceloc(), "{}", msg); } @@ -366,7 +360,7 @@ ASTfunction_declaration::ASTfunction_declaration(OSLCompilerImpl* comp, TypeSpec type, ustring name, ASTNode* form, ASTNode* stmts, ASTNode* meta, - int sourceline_start) + const SrcLoc& srcloc_start) : ASTNode(function_declaration_node, comp, 0, meta, form, stmts) , m_name(name) , m_sym(NULL) @@ -375,8 +369,12 @@ ASTfunction_declaration::ASTfunction_declaration(OSLCompilerImpl* comp, // Some trickery -- the compiler's idea of the "current" source line // is the END of the function body, so if a hint was passed about the // start of the declaration, substitute that. - if (sourceline_start >= 0) - m_sourceline = sourceline_start; + // FIXME: [lfascione] Maybe with the move from int sourceline to SrcLoc this + // is the right thing to do here + if (srcloc_start) { + m_srcloc.line_start = srcloc_start.line_start; + m_srcloc.column_begin = srcloc_start.column_begin; + } if (Strutil::starts_with(name, "___")) errorfmt("\"{}\" : sorry, can't start with three underscores", name); @@ -386,7 +384,9 @@ ASTfunction_declaration::ASTfunction_declaration(OSLCompilerImpl* comp, if (existing_syms && existing_syms->symtype() != SymTypeFunction) { errorfmt("\"{}\" already declared in this scope as a {}", name, existing_syms->typespec()); - // FIXME -- print the file and line of the other definition + // print the file and line of the other definition when available + if (existing_syms->node()) + comp->message_srcloc(existing_syms->node()->sourceloc()); existing_syms = NULL; } @@ -429,13 +429,9 @@ ASTfunction_declaration::ASTfunction_declaration(OSLCompilerImpl* comp, } err += "\n "; if (other) { - err += Strutil::fmt::format( - "{}:{}", - OIIO::Filesystem::filename( - other->sourcefile().string()), - other->sourceline()); + err += Strutil::fmt::format("{}", other->sourceloc()); } else - err += "built-in"; + err += "(built-in)"; } } } @@ -519,7 +515,7 @@ ASTvariable_declaration::ASTvariable_declaration(OSLCompilerImpl* comp, ustring name, ASTNode* init, bool isparam, bool ismeta, bool isoutput, bool initlist, - int sourceline_start) + const SrcLoc& srcloc_start) : ASTNode(variable_declaration_node, comp, 0, init, NULL /* meta */) , m_name(name) , m_sym(NULL) @@ -531,8 +527,10 @@ ASTvariable_declaration::ASTvariable_declaration(OSLCompilerImpl* comp, // Some trickery -- the compiler's idea of the "current" source line // is the END of the declaration, so if a hint was passed about the // start of the declaration, substitute that. - if (sourceline_start >= 0) - m_sourceline = sourceline_start; + if (srcloc_start) { + m_srcloc.line_start = srcloc_start.line_start; + m_srcloc.column_begin = srcloc_start.column_begin; + } if (m_initlist && init) { // Typecheck the init list early. @@ -547,10 +545,8 @@ ASTvariable_declaration::ASTvariable_declaration(OSLCompilerImpl* comp, = Strutil::fmt::format("\"{}\" already declared in this scope", name); if (f->node()) { - std::string filename = OIIO::Filesystem::filename( - f->node()->sourcefile().string()); - e += Strutil::fmt::format("\n\t\tprevious declaration was at {}:{}", - filename, f->node()->sourceline()); + e += Strutil::fmt::format("\n\t\tprevious declaration was at {}", + f->node()->sourceloc()); } if (f->scope() == 0 && f->symtype() == SymTypeFunction && isparam) { // special case: only a warning for param to mask global function diff --git a/src/liboslcomp/ast.h b/src/liboslcomp/ast.h index b9f94a8da..1724940cc 100644 --- a/src/liboslcomp/ast.h +++ b/src/liboslcomp/ast.h @@ -191,15 +191,11 @@ class ASTNode : public OIIO::RefCnt { /// Reverse the order of the list. friend ASTNode::ref reverse(ASTNode::ref list); - /// What source file was this parse node created from? + /// What location in the source file was this parse node created from? /// - ustring sourcefile() const { return m_sourcefile; } + const SrcLoc sourceloc() const { return m_srcloc; } - /// What line of the source file was this parse node created from? - /// - int sourceline() const { return m_sourceline; } - - void sourceline(int line) { m_sourceline = line; } + void sourceloc(const SrcLoc& loc) { m_srcloc = loc; } template void errorfmt(const char* format, const Args&... args) const @@ -407,8 +403,7 @@ class ASTNode : public OIIO::RefCnt { NodeType m_nodetype; ///< Type of node this is ref m_next; ///< Next node in the list OSLCompilerImpl* m_compiler; ///< Back-pointer to the compiler - ustring m_sourcefile; ///< Filename of source where the node came from - int m_sourceline; ///< Line number in source where the node came from + SrcLoc m_srcloc; ///< Location in source where the node came from std::vector m_children; ///< Child nodes int m_op; ///< Operator selection TypeSpec m_typespec; ///< Data type of this node @@ -445,7 +440,7 @@ class ASTfunction_declaration final : public ASTNode { public: ASTfunction_declaration(OSLCompilerImpl* comp, TypeSpec type, ustring name, ASTNode* form, ASTNode* stmts, ASTNode* meta, - int sourceline_start = -1); + const SrcLoc& srcloc_start); const char* nodetypename() const { return "function_declaration"; } const char* childname(size_t i) const; void print(std::ostream& out, int indentlevel = 0) const; @@ -476,7 +471,7 @@ class ASTvariable_declaration final : public ASTNode { ASTvariable_declaration(OSLCompilerImpl* comp, const TypeSpec& type, ustring name, ASTNode* init, bool isparam, bool ismeta, bool isoutput, bool initlist, - int sourceline_start = -1); + const SrcLoc& srcloc_start); const char* nodetypename() const; const char* childname(size_t i) const; void print(std::ostream& out, int indentlevel = 0) const; diff --git a/src/liboslcomp/codegen.cpp b/src/liboslcomp/codegen.cpp index 1c3be53c2..e1ec1771f 100644 --- a/src/liboslcomp/codegen.cpp +++ b/src/liboslcomp/codegen.cpp @@ -65,7 +65,7 @@ OSLCompilerImpl::insert_code(int opnum, const char* opname, size_t nargs, { Opcode op(ustring(opname), m_codegenmethod, m_opargs.size(), nargs); if (node) - op.source(node->sourcefile(), node->sourceline()); + op.source(node->sourceloc()); m_ircode.insert(m_ircode.begin() + opnum, op); add_op_args(nargs, args); @@ -136,8 +136,7 @@ OSLCompilerImpl::add_struct_fields(StructSpec* structspec, ustring basename, int arr = type.arraylength(); if (arr && arraylen) { errorfmt( - node ? node->sourcefile() : ustring(), - node ? node->sourceline() : 1, + node ? node->sourceloc() : SrcLoc(), "Nested structs with >1 levels of arrays are not allowed: {}", structspec->name()); } diff --git a/src/liboslcomp/oslcomp.cpp b/src/liboslcomp/oslcomp.cpp index 14bee9a1f..208762d7d 100644 --- a/src/liboslcomp/oslcomp.cpp +++ b/src/liboslcomp/oslcomp.cpp @@ -130,7 +130,9 @@ OSLCompilerImpl::preprocess_file(const std::string& filename, // Read file contents into a string std::string instring; if (!OIIO::Filesystem::read_text_file(filename, instring)) { - errorfmt(ustring(filename), 0, "Could not open \"{}\"\n", filename); + SrcLoc srcloc; + srcloc.filename = filename; + errorfmt(srcloc, "Could not open \"{}\"\n", filename); return false; } return preprocess_buffer(instring, filename, stdoslpath, defines, @@ -243,7 +245,7 @@ OSLCompilerImpl::preprocess_buffer(const std::string& buffer, while (preproc_errors.size() && preproc_errors[preproc_errors.size() - 1] == '\n') preproc_errors.erase(preproc_errors.size() - 1); - errorfmt(ustring(), -1, "{}", preproc_errors); + errorfmt(SrcLoc(), "{}", preproc_errors); return false; } return true; @@ -405,7 +407,7 @@ OSLCompilerImpl::compile(string_view filename, string_view stdoslpath) { if (!OIIO::Filesystem::exists(filename)) { - errorfmt(ustring(), 0, "Input file \"{}\" not found", filename); + errorfmt(SrcLoc(), "Input file \"{}\" not found", filename); return false; } @@ -422,9 +424,11 @@ OSLCompilerImpl::compile(string_view filename, if (stdoslpath.empty()) { stdoslpath = find_stdoslpath(includepaths); } - if (stdoslpath.empty() || !OIIO::Filesystem::exists(stdoslpath)) - warningfmt(ustring(filename), 0, "Unable to find \"stdosl.h\""); - else { + if (stdoslpath.empty() || !OIIO::Filesystem::exists(stdoslpath)) { + SrcLoc srcloc; + srcloc.filename = filename; + warningfmt(srcloc, "Unable to find \"stdosl.h\""); + } else { // Add the directory of stdosl.h to the include paths includepaths.push_back(OIIO::Filesystem::parent_path(stdoslpath)); } @@ -443,7 +447,7 @@ OSLCompilerImpl::compile(string_view filename, if (shader()) shader()->typecheck(); else - errorfmt(ustring(), 0, "No shader function defined"); + errorfmt(SrcLoc(), "No shader function defined"); } // Print the parse tree if there were no errors @@ -472,7 +476,7 @@ OSLCompilerImpl::compile(string_view filename, OIIO::ofstream oso_output; OIIO::Filesystem::open(oso_output, m_output_filename); if (!oso_output.good()) { - errorfmt(ustring(), 0, "Could not open \"{}\"", + errorfmt(SrcLoc(), "Could not open \"{}\"", m_output_filename); return false; } @@ -485,7 +489,7 @@ OSLCompilerImpl::compile(string_view filename, oso_output.close(); if (!oso_output.good()) { - errorfmt(ustring(), 0, "Failed to write to \"{}\"", + errorfmt(SrcLoc(), "Failed to write to \"{}\"", m_output_filename); return false; } @@ -518,8 +522,11 @@ OSLCompilerImpl::compile_buffer(string_view sourcecode, std::string& osobuffer, if (stdoslpath.empty()) { stdoslpath = find_stdoslpath(includepaths); } - if (stdoslpath.empty() || !OIIO::Filesystem::exists(stdoslpath)) - warningfmt(ustring(filename), 0, "Unable to find \"stdosl.h\""); + if (stdoslpath.empty() || !OIIO::Filesystem::exists(stdoslpath)) { + SrcLoc srcloc; + srcloc.filename = filename; + warningfmt(srcloc, "Unable to find \"stdosl.h\""); + } std::string preprocess_result; if (!preprocess_buffer(sourcecode, filename, stdoslpath, defines, @@ -535,7 +542,7 @@ OSLCompilerImpl::compile_buffer(string_view sourcecode, std::string& osobuffer, if (shader()) shader()->typecheck(); else - errorfmt(ustring(), 0, "No shader function defined"); + errorfmt(SrcLoc(), "No shader function defined"); } // Print the parse tree if there were no errors @@ -614,7 +621,7 @@ OSLCompilerImpl::write_dependency_file(string_view filename) if (depfile != stdout) fclose(depfile); } else { - errorfmt(ustring(), 0, + errorfmt(SrcLoc(), "Could not open dependency file '{}' for writing", m_deps_filename); } @@ -679,9 +686,14 @@ OSLCompilerImpl::write_oso_metadata(const ASTNode* metanode) const bool ok = metavar->param_default_literals(metasym, metavar->init().get(), pdl, ","); if (ok) { + if (OIIO::Strutil::starts_with(metasym->name(), "osl_")) + warningfmt( + metanode->sourceloc(), + "Metadata names starting with \"osl_\" are reserved for internal OSL use ({})", + metasym->name()); osofmt("%meta{{{},{},{}}} ", ts, metasym->name(), pdl); } else { - errorfmt(metanode->sourcefile(), metanode->sourceline(), + errorfmt(metanode->sourceloc(), "Don't know how to print metadata {} ({}) with node type {}", metasym->name(), ts, metavar->init()->nodetypename()); } @@ -849,13 +861,16 @@ OSLCompilerImpl::write_oso_file(string_view options, osofmt("# options: {}\n", options); ASTshader_declaration* shaderdecl = shader_decl(); - osofmt("{} {}", shaderdecl->shadertypename(), shaderdecl->shadername()); + osofmt("{} {}\t", shaderdecl->shadertypename(), shaderdecl->shadername()); // output global hints and metadata - int hints = 0; + { + osofmt("%meta{{{},{},\"{}\"}} ", TypeString, "osl_version", + OSL_LIBRARY_VERSION_STRING); + osofmt("%meta{{{},{},\"{}\"}} ", TypeString, "osl_compile_options", options); + } + for (ASTNode::ref m = shaderdecl->metadata(); m; m = m->next()) { - if (hints++ == 0) - osofmt("\t"); write_oso_metadata(m.get()); } @@ -878,7 +893,7 @@ OSLCompilerImpl::write_oso_file(string_view options, } // Output all opcodes - int lastline = -1; + SrcLoc lastsrcloc; ustring lastfile; ustring lastmethod("___uninitialized___"); for (auto& op : m_ircode) { @@ -886,15 +901,15 @@ OSLCompilerImpl::write_oso_file(string_view options, osofmt("code {}\n", op.method()); lastmethod = op.method(); lastfile = ustring(); - lastline = -1; + lastsrcloc = SrcLoc(); } if (/*m_debug &&*/ !op.sourcefile().empty()) { ustring file = op.sourcefile(); - int line = op.sourceline(); - if (file != lastfile || line != lastline) - osofmt("# {}:{}\n# {}\n", file, line, - retrieve_source(file, line)); + SrcLoc srcloc = op.srcloc(); + if (file != lastfile || srcloc != lastsrcloc) + osofmt("# {}:{}:{}\n# {}\n", file, srcloc.first_lineno(), srcloc.first_colno(), + retrieve_source_lines(srcloc)); } // Op name @@ -922,18 +937,30 @@ OSLCompilerImpl::write_oso_file(string_view options, // %filename and %line document the source code file and line that // contained code that generated this op. To avoid clutter, we // only output these hints when they DIFFER from the previous op. - if (!op.sourcefile().empty()) { - if (op.sourcefile() != lastfile) { - lastfile = op.sourcefile(); + if (op.srcloc()) { + if (op.srcloc().filename != lastfile) { + lastfile = op.srcloc().filename; osofmt("{}%filename{{\"{}\"}}", firsthint ? '\t' : ' ', lastfile); firsthint = false; } - if (op.sourceline() != lastline) { - lastline = op.sourceline(); - osofmt("{}%line{{{}}}", firsthint ? '\t' : ' ', lastline); + if (op.srcloc().last_lineno() != lastsrcloc.last_lineno()) { + osofmt("{}%line{{{}}}", firsthint ? '\t' : ' ', op.srcloc().last_lineno()); + firsthint = false; + } + if (op.srcloc() != lastsrcloc) { + // n.b.: we need to use last_colno()+1 because the parser will use + // srcloc.from_yyloc() to pull in this data, and so we want it to be + // in that representation (1-based, [begin,end) style) but our + // last_colno() is inclusive (the methods return [first,last]) + // I'll be the first to admit this is confusing + osofmt("{}%srcloc{{{},{},{},{}}}", firsthint ? '\t' : ' ', + lastsrcloc.first_lineno(), lastsrcloc.first_colno(), + lastsrcloc.last_lineno(), lastsrcloc.last_colno()+1); firsthint = false; } + // note this is unconditional + lastsrcloc = op.srcloc(); } // %argrw documents which arguments are read, written, or both (rwW). @@ -980,71 +1007,80 @@ OSLCompilerImpl::write_oso_file(string_view options, } +OSLCompilerImpl::FileLines::FileLines(ustring filename) +{ + // Read the file and prepare its line lookup table + // this eager idea is ok, because either this code is used when OSO is emitted, + // and so the majority of the lines are needed anyways, or it's used in error + // reporting where the small time penalty is not important + bool ok = OIIO::Filesystem::read_text_file(filename, text); + if (ok) { + lines.push_back(0); // line 0 starts at 0 + // split lines + const char* base = text.c_str(); // help the compiler optimize the loop + for (uint32_t i = 0, e = text.size(); i < e; ++i) + if (base[i] == '\n') + lines.push_back(i + 1); // line starts on the _next_ character! + + } else { + text = ""; + } +} + + void OSLCompilerImpl::clear_filecontents_cache() { m_filecontents_map.clear(); m_last_sourcefile.clear(); - m_last_filecontents = nullptr; - m_last_sourceline = 1; // note we call the first line "1" for users - m_last_sourceline_offset = 0; + m_last_filelines = nullptr; } string_view -OSLCompilerImpl::retrieve_source(ustring filename, int line) +OSLCompilerImpl::retrieve_source_impl(const SrcLoc& srcloc, + bool full_lines) const { // If we don't have a valid "last", look it up in the cache. - if (filename != m_last_sourcefile || !m_last_filecontents) { - m_last_sourceline = 1; - m_last_sourceline_offset = 0; - auto found = m_filecontents_map.find(filename); - if (found == m_filecontents_map.end()) { + if (srcloc.filename != m_last_sourcefile || !m_last_filelines) { + auto it = m_filecontents_map.find(srcloc.filename); + if (it == m_filecontents_map.end()) { // If it wasn't in the cache, read the file and add it. - std::string contents; - bool ok = OIIO::Filesystem::read_text_file(filename, contents); - if (ok) { - m_last_sourcefile = filename; - m_filecontents_map[filename] = std::move(contents); - m_last_filecontents = &m_filecontents_map[filename]; - } else { - m_last_sourcefile = ustring(); - m_last_filecontents = nullptr; - return ""; - } - } else { - m_last_sourcefile = filename; - m_last_filecontents = &found->second; + it = m_filecontents_map.emplace(srcloc.filename, srcloc.filename).first; } + + m_last_sourcefile = srcloc.filename; + m_last_filelines = &it->second; } + // file not found has an appropriate "file not found" text string inside + if (!*m_last_filelines) + return m_last_filelines->text; + + // first/last line are 1-based and inclusive, whereas we turn them into + // 0-based, [begin,end) like an STL range + uint32_t begin_line = srcloc.line_start; + uint32_t end_line = srcloc.line_stop + 1; + if (begin_line >= m_last_filelines->lines.size()) + return ""; + // Now read lines up to and including the file we want. - OIIO::string_view s(*m_last_filecontents); - int orig_sourceline = line; - if (line >= m_last_sourceline) { - // Shortcut: the line we want is in the same file as the last read, - // and at least as far in the file. Start the search from where we - // left off last time. - s.remove_prefix(m_last_sourceline_offset); - line -= m_last_sourceline - 1; - } else { - // If we have to backtrack at all, backtrack to the file start. - m_last_sourceline_offset = 0; - m_last_sourceline = 1; - } - size_t offset = m_last_sourceline_offset; - for (; line > 1; --line) { - size_t p = s.find_first_of('\n'); - if (p == OIIO::string_view::npos) - return ""; - s.remove_prefix(p + 1); - offset += p + 1; + string_view s(m_last_filelines->text); + size_t begin_offset = m_last_filelines->lines[begin_line]; + size_t end_offset = m_last_filelines->lines[end_line]; + if (full_lines == 0) { + begin_offset += srcloc.column_begin; + // the end needs to be one line before, plus the column_end + end_offset = m_last_filelines->lines[end_line - 1] + + srcloc.column_end; } - s = s.substr(0, s.find_first_of('\n')); - m_last_sourceline_offset = offset; - m_last_sourceline = orig_sourceline; + s = s.substr(begin_offset, end_offset - begin_offset); + // drop the last trailing newline if there + if (s.back() == '\n') + s.remove_suffix(1); + return s; } @@ -1123,14 +1159,14 @@ OSLCompilerImpl::check_write_legality(const Opcode& op, int opnum, { // We can never write to constant symbols if (sym->symtype() == SymTypeConst) { - errorfmt(op.sourcefile(), op.sourceline(), + errorfmt(op.srcloc(), "Attempted to write to a constant value"); } // Params can only write if it's part of their initialization if (sym->symtype() == SymTypeParam && (opnum < sym->initbegin() || opnum >= sym->initend())) { - errorfmt(op.sourcefile(), op.sourceline(), + errorfmt(op.srcloc(), "cannot write to non-output parameter \"{}\"", sym->name()); } } diff --git a/src/liboslcomp/oslcomp_pvt.h b/src/liboslcomp/oslcomp_pvt.h index f5ee5f787..e8671e68d 100644 --- a/src/liboslcomp/oslcomp_pvt.h +++ b/src/liboslcomp/oslcomp_pvt.h @@ -49,95 +49,141 @@ class OSLCompilerImpl { bool osl_parse_buffer(const std::string& preprocessed_buffer); - /// The name of the file we're currently parsing - /// - ustring filename() const { return m_filename; } - /// Set the name of the file we're currently parsing (should only /// be called by the lexer!) - void filename(ustring f) + void update_filename(ustring f) { - m_filename = f; + m_srcloc.filename = f; m_file_dependencies.emplace(f); } - /// The line we're currently parsing - /// - int lineno() const { return m_lineno; } + /// Update the location we're currently parsing + // Often times this code is written in the Flex file, but we keep it here instead + // to clarify its special relation to SrcLoc::from_yyloc(), as the two are joined + // at the hip anyways. In the Flex file this is invoked by the YY_USER_ACTION macro. + // When the function returns, the four values {first,last}{colum,line} bound the + // `text` that was passed in + void update_srcloc(int* first_line, int* first_column, int* last_line, + int* last_column, const char* text) + { + // update the state of the parser + *first_line = *last_line; + *first_column = *last_column; + + for (int i = 0; text[i] != '\0'; i++) { + if (text[i] == '\n') { + (*last_line)++; + *last_column = 1; + } else { + (*last_column)++; + } + } - /// Set the line we're currently parsing (should only be called by - /// the lexer!) - void lineno(int l) { m_lineno = l; } + m_srcloc.from_yyloc(*first_line, *first_column, *last_line, + *last_column); + } - /// Increment the line count - /// - int incr_lineno() { return ++m_lineno; } + /// Retrieve the location of the lexeme/segment currently being parsed, as well as its filename + const SrcLoc& srcloc() const { return m_srcloc; } ErrorHandler& errhandler() const { return *m_errhandler; } + /// Support for errorfmt/warningfmt/etc... + void message_srcloc(const SrcLoc& srcloc) const + { + string_view lines = retrieve_source_lines(srcloc); + m_errhandler->messagefmt("{}\n", lines); + // see if we can underline the span, + // N.B. this simple method will only work on srclocs spanning one line, + // multiline is rather messier to do well, as we'd probably want to interleave + // the source text with the underlining + if (srcloc.column_begin != SrcLoc::kUnknown) { + std::string marker(lines.substr(0, srcloc.column_begin)); + // replace all non-whitespace with one space so we + // don't mess up alignment if there are tabs + for (char& c : marker) + if (isgraph(c)) + c = ' '; + + marker += std::string(srcloc.colcount(), '^'); + m_errhandler->messagefmt("{}\n", marker); + } + } + /// Error reporting template - void errorfmt(ustring filename, int line, const char* format, + void errorfmt(const SrcLoc& srcloc, const char* format, const Args&... args) const { OSL_DASSERT(format && format[0]); std::string msg = OIIO::Strutil::fmt::format(format, args...); if (msg.size() && msg.back() == '\n') // trim extra newline msg.pop_back(); - if (filename.size()) - m_errhandler->errorfmt("{}:{}: error: {}", filename, line, msg); - else + if (srcloc) { + m_errhandler->errorfmt("{}: error: {}", srcloc, msg); + message_srcloc(srcloc); + } else { m_errhandler->errorfmt("error: {}", msg); + } + m_err = true; } /// Warning reporting template - void warningfmt(ustring filename, int line, const char* format, + void warningfmt(const SrcLoc& srcloc, const char* format, const Args&... args) const { OSL_DASSERT(format && format[0]); - if (nowarn(filename, line)) + if (nowarn(srcloc)) return; // skip if the filename/line is on the nowarn list std::string msg = OIIO::Strutil::fmt::format(format, args...); - if (msg.size() && msg.back() == '\n') // trim extra newline - msg.pop_back(); if (m_err_on_warning) { - errorfmt(filename, line, "{}", msg); + errorfmt(srcloc, "{}", msg); return; } - if (filename.size()) - m_errhandler->warningfmt("{}:{}: warning: {}", filename, line, msg); + if (msg.size() && msg.back() == '\n') // trim extra newline + msg.pop_back(); + + if (srcloc) { + m_errhandler->warningfmt("{}: warning: {}", srcloc, msg); + message_srcloc(srcloc); + } else m_errhandler->warningfmt("warning: {}", msg); } /// Info reporting template - void infofmt(ustring filename, int line, const char* format, + void infofmt(const SrcLoc& srcloc, const char* format, const Args&... args) const { OSL_DASSERT(format && format[0]); std::string msg = OIIO::Strutil::fmt::format(format, args...); if (msg.size() && msg.back() == '\n') // trim extra newline msg.pop_back(); - if (filename.size()) - m_errhandler->infofmt("{}:{}: info: {}", filename, line, msg); + + if (srcloc) { + m_errhandler->infofmt("{}: info: {}", srcloc, msg); + message_srcloc(srcloc); + } else - m_errhandler->infofmt("info: {}", msg); + m_errhandler->infofmt("info: {}", srcloc, msg); } /// message reporting template - void messagefmt(ustring filename, int line, const char* format, + void messagefmt(const SrcLoc& srcloc, const char* format, const Args&... args) const { OSL_DASSERT(format && format[0]); std::string msg = OIIO::Strutil::fmt::format(format, args...); if (msg.size() && msg.back() == '\n') // trim extra newline msg.pop_back(); - if (filename.size()) - m_errhandler->messagefmt("{}:{}: {}", filename, line, msg); + if (srcloc) { + m_errhandler->messagefmt("{}: {}", srcloc, msg); + message_srcloc(srcloc); + } else m_errhandler->messagefmt("{}", msg); } @@ -339,13 +385,14 @@ class OSLCompilerImpl { // Add a pragma nowarn for the following line void pragma_nowarn() { - m_nowarn_lines.insert({ filename(), lineno() + 1 }); + // we use human numbers, we need the following line + m_nowarn_lines.insert({ m_srcloc.filename, m_srcloc.last_lineno() + 1 }); } // Is the line among the 'do not warn' list - bool nowarn(ustring filename, int line) const + bool nowarn(const SrcLoc& srcloc) const { - return m_nowarn_lines.find({ filename, line }) != m_nowarn_lines.end(); + return m_nowarn_lines.find({ srcloc.filename, srcloc.last_lineno() }) != m_nowarn_lines.end(); } std::stack& typespec_stack() { return m_typespec_stack; } @@ -419,14 +466,25 @@ class OSLCompilerImpl { return static_cast(m_shader.get()); } - // Retrieve the particular line of a file. - string_view retrieve_source(ustring filename, int line); + string_view retrieve_source_impl(const SrcLoc& srcloc, + bool full_lines) const; + + /// Retrieve the given location range from a file, in full lines + string_view retrieve_source_lines(const SrcLoc& srcloc) const + { + return retrieve_source_impl(srcloc, true); + } + + /// Retrieve the given location range from a file, just the covered tokens + string_view retrieve_source_tokens(const SrcLoc& srcloc) const + { + return retrieve_source_impl(srcloc, false); + } - // Clear internal caches that speed up retrieve_source(). + // Clear internal caches that speed up retrieve_source_xxx(). void clear_filecontents_cache(); - ustring m_filename; ///< Current file we're parsing - int m_lineno; ///< Current line we're parsing + SrcLoc m_srcloc; ///< Location of the lexeme/segment being parsed std::string m_output_filename; ///< Output filename ustring m_main_filename; ///< Main input filename std::string m_cwd; ///< Current working directory @@ -461,12 +519,19 @@ class OSLCompilerImpl { int m_main_method_start; ///< Instruction where 'main' starts bool m_declaring_shader_formals; ///< Are we declaring shader formals? std::set> m_nowarn_lines; ///< Lines for 'nowarn' - typedef std::unordered_map FCMap; - FCMap m_filecontents_map; ///< Contents of source files we've seen - ustring m_last_sourcefile; ///< Last filename for retrieve_source - std::string* m_last_filecontents = nullptr; //< Last file contents - int m_last_sourceline; - size_t m_last_sourceline_offset; + + struct FileLines { + std::string text; + std::vector lines; + FileLines(ustring fname); + + explicit operator bool() const { return !lines.empty(); } + }; + typedef std::unordered_map FCMap; + mutable FCMap m_filecontents_map; ///< Contents of source files we've seen + mutable ustring m_last_sourcefile; ///< Last filename for retrieve_source + mutable FileLines* m_last_filelines = nullptr; ///< Last file contents + std::string m_deps_filename; ///< Where to write deps? -MF std::string m_deps_target; ///< Custom target: -MF std::set m_file_dependencies; ///< All include file dependencies diff --git a/src/liboslcomp/oslgram.y b/src/liboslcomp/oslgram.y index 715ab77f8..1c802b743 100644 --- a/src/liboslcomp/oslgram.y +++ b/src/liboslcomp/oslgram.y @@ -45,13 +45,15 @@ using namespace OSL::pvt; // This is the definition for the union that defines YYSTYPE %union { - int i; // For integer falues + int i; // For integer values float f; // For float values ASTNode *n; // Abstract Syntax Tree node const char *s; // For string values -- guaranteed to be a ustring.c_str() } %{ +#define SRCLOC(yyloc) SrcLoc(oslcompiler->srcloc().filename, yyloc.first_line, yyloc.first_column, yyloc.last_line, yyloc.last_column) + OSL_NAMESPACE_BEGIN namespace pvt { @@ -184,7 +186,7 @@ shader_or_function_declaration ustring($2), $7 /*formals*/, $11 /*statements*/, NULL /*metadata*/, - @2.first_line); + SRCLOC(@2)); oslcompiler->remember_function_decl (f); f->add_meta (concat($4, $10)); $$ = f; @@ -195,7 +197,7 @@ shader_or_function_declaration ustring($2), $7 /*formals*/, $11 /*statements*/, concat($4,$10) /*meta*/); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); if (oslcompiler->shader_is_defined()) { yyerror (&yylloc, scanner, oslcompiler, YY_("Only one shader is allowed per file.")); delete $$; @@ -227,7 +229,7 @@ formal_param t, ustring($3), $4 /*init*/, oslcompiler->declaring_shader_formals() /*isparam*/, false /*ismeta*/, is_output, - false /*instlist*/, @3.first_line); + false /*instlist*/, SRCLOC(@3)); if (! oslcompiler->declaring_shader_formals() && !is_output) { // these are function formals, not shader formals, var->sym()->readonly (true); @@ -244,7 +246,7 @@ formal_param ustring($3), $5 /*init*/, oslcompiler->declaring_shader_formals() /*isparam*/, false /*ismeta*/, $1 /*isoutput*/, - true /* initlist */, @3.first_line); + true /* initlist */, SRCLOC(@3)); var->add_meta ($6); $$ = var; } @@ -253,8 +255,7 @@ formal_param // Grab the current declaration type, modify it to be array TypeSpec t = oslcompiler->current_typespec(); if (! t.is_structure() && ! t.is_triple() && ! t.is_matrix()) - oslcompiler->errorfmt(oslcompiler->filename(), - oslcompiler->lineno(), + oslcompiler->errorfmt(oslcompiler->srcloc(), "Can't use '= {{...}}' initializer " "except with arrays, structs, vectors, " "or matrix ({})", $3); @@ -263,7 +264,7 @@ formal_param oslcompiler->declaring_shader_formals() /*isparam*/, false /*ismeta*/, $1 /*isoutput*/, true /* initializer list */, - @3.first_line); + SRCLOC(@3)); var->add_meta ($5); $$ = var; } @@ -291,7 +292,7 @@ metadatum true /* ismeta */, false /* isoutput */, false /* initlist */, - @2.first_line); + SRCLOC(@2)); $$ = var; } | simple_typename IDENTIFIER arrayspec initializer_list @@ -299,15 +300,14 @@ metadatum TypeDesc simple = osllextype ($1); simple.arraylen = $3; if (simple.arraylen < 1) - oslcompiler->errorfmt(oslcompiler->filename(), - oslcompiler->lineno(), + oslcompiler->errorfmt(oslcompiler->srcloc(), "Invalid array length for {}", $2); TypeSpec t (simple, false); auto var = new ASTvariable_declaration (oslcompiler, t, ustring ($2), $4, false, - true /* ismeata */, false /* output */, + true /* ismeta */, false /* output */, true /* initializer list */, - @2.first_line); + SRCLOC(@2)); $$ = var; } ; @@ -329,7 +329,7 @@ function_declaration auto f = new ASTfunction_declaration (oslcompiler, oslcompiler->typespec_stack().top(), ustring($2), $5, $8, NULL, - @2.first_line); + SRCLOC(@2)); oslcompiler->remember_function_decl (f); f->add_meta ($7); $$ = f; @@ -343,12 +343,12 @@ struct_declaration ustring name ($2); Symbol *s = oslcompiler->symtab().clash (name); if (s) { - oslcompiler->errorfmt(oslcompiler->filename(), oslcompiler->lineno(), + oslcompiler->errorfmt(oslcompiler->srcloc(), "\"{}\" already declared in this scope", name); // FIXME -- print the file and line of the other definition } if (OIIO::Strutil::starts_with (name, "___")) { - oslcompiler->errorfmt(oslcompiler->filename(), oslcompiler->lineno(), + oslcompiler->errorfmt(oslcompiler->srcloc(), "\"{}\" : sorry, can't start with three underscores", name); } oslcompiler->symtab().new_struct (name); @@ -380,8 +380,7 @@ typed_field TypeSpec t = oslcompiler->current_typespec(); StructSpec *s = oslcompiler->symtab().current_struct(); if (s->lookup_field (name) >= 0) - oslcompiler->errorfmt(oslcompiler->filename(), - oslcompiler->lineno(), + oslcompiler->errorfmt(oslcompiler->srcloc(), "Field \"{}\" already exists in struct \"{}\"", name, s->name()); else @@ -395,13 +394,11 @@ typed_field TypeSpec t = oslcompiler->current_typespec(); t.make_array ($2); if (t.arraylength() < 1) - oslcompiler->errorfmt(oslcompiler->filename(), - oslcompiler->lineno(), + oslcompiler->errorfmt(oslcompiler->srcloc(), "Invalid array length for {}", name); StructSpec *s = oslcompiler->symtab().current_struct(); if (s->lookup_field (name) >= 0) - oslcompiler->errorfmt(oslcompiler->filename(), - oslcompiler->lineno(), + oslcompiler->errorfmt(oslcompiler->srcloc(), "Field \"{}\" already exists in struct \"{}\"", name, s->name()); else @@ -431,7 +428,7 @@ def_expression $$ = new ASTvariable_declaration (oslcompiler, t, ustring($1), $2, false /*isparam*/, false /*ismeta*/, false /*isoutput*/, - false /*initlist*/, @1.first_line); + false /*initlist*/, SRCLOC(@1)); } | IDENTIFIER arrayspec initializer_list_opt { @@ -439,25 +436,23 @@ def_expression TypeSpec t = oslcompiler->current_typespec(); t.make_array ($2); if ($2 < 1) - oslcompiler->errorfmt(oslcompiler->filename(), - oslcompiler->lineno(), + oslcompiler->errorfmt(oslcompiler->srcloc(), "Invalid array length for {}", $1); $$ = new ASTvariable_declaration (oslcompiler, t, ustring($1), $3, false, false, false, - true /* initlist */, @1.first_line); + true /* initlist */, SRCLOC(@1)); } | IDENTIFIER initializer_list { TypeSpec t = oslcompiler->current_typespec(); if (! t.is_structure() && ! t.is_triple() && ! t.is_matrix()) - oslcompiler->errorfmt(oslcompiler->filename(), - oslcompiler->lineno(), + oslcompiler->errorfmt(oslcompiler->srcloc(), "Can't use '= {{...}}' initializer " "except with arrays, struct, vectors, " "or matrix ({})", $1); $$ = new ASTvariable_declaration (oslcompiler, t, ustring($1), $2, false, false, false, - true /* initlist */, @1.first_line); + true /* initlist */, SRCLOC(@1)); } ; @@ -483,17 +478,16 @@ compound_initializer : '{' init_expression_list '}' { $$ = new ASTcompound_initializer (oslcompiler, $2); - $$->sourceline (@1.first_line); + $$->sourceloc(SRCLOC(@1)); } | '{' '}' { if (!oslcompiler->declaring_shader_formals()) - oslcompiler->errorfmt(oslcompiler->filename(), - oslcompiler->lineno(), + oslcompiler->errorfmt(oslcompiler->srcloc(), "Empty compound initializers '{{ }}' " "only allowed for shader parameters."); $$ = new ASTcompound_initializer (oslcompiler, nullptr); - $$->sourceline (@1.first_line); + $$->sourceloc(SRCLOC(@1)); } ; @@ -571,8 +565,7 @@ arrayspec : '[' INT_LITERAL ']' { if ($2 < 1) - oslcompiler->errorfmt(oslcompiler->filename(), - oslcompiler->lineno(), + oslcompiler->errorfmt(oslcompiler->srcloc(), "Invalid array length ({})", $2); $$ = $2; } @@ -599,8 +592,7 @@ typespec oslcompiler->current_typespec (TypeSpec ("", s->typespec().structure())); else { oslcompiler->current_typespec (TypeSpec (TypeDesc::UNKNOWN)); - oslcompiler->errorfmt(oslcompiler->filename(), - oslcompiler->lineno(), + oslcompiler->errorfmt(oslcompiler->srcloc(), "Unknown struct name: {}", $1); } $$ = 0; @@ -640,8 +632,7 @@ typespec_or_shadertype oslcompiler->current_typespec (TypeSpec ("", s->typespec().structure())); else { oslcompiler->current_typespec (TypeSpec (TypeDesc::UNKNOWN)); - oslcompiler->errorfmt(oslcompiler->filename(), - oslcompiler->lineno(), + oslcompiler->errorfmt(oslcompiler->srcloc(), "Unknown struct name: {}", $1); } $$ = (int)ShaderType::Unknown; @@ -681,12 +672,12 @@ conditional_statement : IF_TOKEN '(' compound_expression ')' statement { $$ = new ASTconditional_statement (oslcompiler, $3, $5); - $$->sourceline (@1.first_line); + $$->sourceloc(SRCLOC(@1)); } | IF_TOKEN '(' compound_expression ')' statement ELSE statement { $$ = new ASTconditional_statement (oslcompiler, $3, $5, $7); - $$->sourceline (@1.first_line); + $$->sourceloc(SRCLOC(@1)); } ; @@ -696,14 +687,14 @@ loop_statement $$ = new ASTloop_statement (oslcompiler, ASTloop_statement::LoopWhile, NULL, $3, NULL, $5); - $$->sourceline (@1.first_line); + $$->sourceloc(SRCLOC(@1)); } | DO statement WHILE '(' compound_expression ')' ';' { $$ = new ASTloop_statement (oslcompiler, ASTloop_statement::LoopDo, NULL, $5, NULL, $2); - $$->sourceline (@1.first_line); + $$->sourceloc(SRCLOC(@1)); } | FOR '(' { @@ -714,7 +705,7 @@ loop_statement $$ = new ASTloop_statement (oslcompiler, ASTloop_statement::LoopFor, $4, $5, $7, $9); - $$->sourceline (@1.first_line); + $$->sourceloc(SRCLOC(@1)); oslcompiler->symtab().pop (); } ; @@ -790,7 +781,7 @@ expression $$ = $2; } else { $$ = new ASTunary_expression (oslcompiler, $1, $2); - $$->sourceline (@1.first_line); + $$->sourceloc(SRCLOC(@1)); } } | '(' compound_expression ')' @@ -802,8 +793,7 @@ expression // color x = Cd * (a, b, c); // same as: x = Cd * c // when they really meant // color x = Cd * color(a, b, c); - oslcompiler->warningfmt(oslcompiler->filename(), - @1.first_line, + oslcompiler->warningfmt(SRCLOC(@1), "Comma operator inside parenthesis is probably an error -- it is not a vector/color."); } $$ = $2; @@ -820,17 +810,17 @@ variable_lvalue | id_or_field '[' expression ']' { $$ = new ASTindex (oslcompiler, $1, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | id_or_field '[' expression ']' '[' expression ']' { $$ = new ASTindex (oslcompiler, $1, $3, $6); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | id_or_field '[' expression ']' '[' expression ']' '[' expression ']' { $$ = new ASTindex (oslcompiler, $1, $3, $6, $9); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } ; @@ -842,7 +832,7 @@ id_or_field | variable_lvalue '.' IDENTIFIER { $$ = new ASTstructselect (oslcompiler, $1, ustring($3)); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } ; @@ -861,109 +851,109 @@ binary_expression { $$ = ASTbinary_expression::make(oslcompiler, ASTNode::Or, $1, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | expression AND_OP expression { $$ = ASTbinary_expression::make(oslcompiler, ASTNode::And, $1, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | expression '|' expression { $$ = ASTbinary_expression::make(oslcompiler, ASTNode::BitOr, $1, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | expression '^' expression { $$ = ASTbinary_expression::make(oslcompiler, ASTNode::Xor, $1, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | expression '&' expression { $$ = ASTbinary_expression::make(oslcompiler, ASTNode::BitAnd, $1, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | expression EQ_OP expression { $$ = ASTbinary_expression::make(oslcompiler, ASTNode::Equal, $1, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | expression NE_OP expression { $$ = ASTbinary_expression::make(oslcompiler, ASTNode::NotEqual, $1, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | expression '>' expression { $$ = ASTbinary_expression::make(oslcompiler, ASTNode::Greater, $1, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | expression GE_OP expression { $$ = ASTbinary_expression::make(oslcompiler, ASTNode::GreaterEqual, $1, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | expression '<' expression { $$ = ASTbinary_expression::make(oslcompiler, ASTNode::Less, $1, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | expression LE_OP expression { $$ = ASTbinary_expression::make(oslcompiler, ASTNode::LessEqual, $1, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | expression SHL_OP expression { $$ = ASTbinary_expression::make(oslcompiler, ASTNode::ShiftLeft, $1, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | expression SHR_OP expression { $$ = ASTbinary_expression::make(oslcompiler, ASTNode::ShiftRight, $1, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | expression '+' expression { $$ = ASTbinary_expression::make(oslcompiler, ASTNode::Add, $1, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | expression '-' expression { $$ = ASTbinary_expression::make(oslcompiler, ASTNode::Sub, $1, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | expression '*' expression { $$ = ASTbinary_expression::make(oslcompiler, ASTNode::Mul, $1, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | expression '/' expression { $$ = ASTbinary_expression::make(oslcompiler, ASTNode::Div, $1, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | expression '%' expression { $$ = ASTbinary_expression::make(oslcompiler, ASTNode::Mod, $1, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } ; @@ -990,7 +980,7 @@ type_constructor { $$ = new ASTtype_constructor (oslcompiler, TypeSpec (osllextype ($1)), $3); - $$->sourceline (@1.first_line); + $$->sourceloc(SRCLOC(@1)); } ; @@ -998,7 +988,7 @@ function_call : IDENTIFIER '(' function_args_opt ')' { $$ = new ASTfunction_call (oslcompiler, ustring($1), $3); - $$->sourceline (@1.first_line); + $$->sourceloc(SRCLOC(@1)); } ; @@ -1018,52 +1008,52 @@ assign_expression : variable_lvalue '=' expression { $$ = new ASTassign_expression (oslcompiler, $1, ASTNode::Assign, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | variable_lvalue MUL_ASSIGN expression { $$ = new ASTassign_expression (oslcompiler, $1, ASTNode::Mul, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | variable_lvalue DIV_ASSIGN expression { $$ = new ASTassign_expression (oslcompiler, $1, ASTNode::Div, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | variable_lvalue ADD_ASSIGN expression { $$ = new ASTassign_expression (oslcompiler, $1, ASTNode::Add, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | variable_lvalue SUB_ASSIGN expression { $$ = new ASTassign_expression (oslcompiler, $1, ASTNode::Sub, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | variable_lvalue BIT_AND_ASSIGN expression { $$ = new ASTassign_expression (oslcompiler, $1, ASTNode::BitAnd, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | variable_lvalue BIT_OR_ASSIGN expression { $$ = new ASTassign_expression (oslcompiler, $1, ASTNode::BitOr, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | variable_lvalue XOR_ASSIGN expression { $$ = new ASTassign_expression (oslcompiler, $1, ASTNode::Xor, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | variable_lvalue SHL_ASSIGN expression { $$ = new ASTassign_expression (oslcompiler, $1, ASTNode::ShiftLeft, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } | variable_lvalue SHR_ASSIGN expression { $$ = new ASTassign_expression (oslcompiler, $1, ASTNode::ShiftRight, $3); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } ; @@ -1071,7 +1061,7 @@ ternary_expression : expression '?' expression ':' expression { $$ = new ASTternary_expression (oslcompiler, $1, $3, $5); - $$->sourceline (@2.first_line); + $$->sourceloc(SRCLOC(@2)); } ; @@ -1099,7 +1089,7 @@ string_literal_group void OSL::pvt::yyerror (YYLTYPE* yylloc_param, void* yyscanner, OSLCompilerImpl* oslcompiler, const char* err) { - oslcompiler->errorfmt(oslcompiler->filename(), oslcompiler->lineno(), + oslcompiler->errorfmt(oslcompiler->srcloc(), "Syntax error: {}", err); } diff --git a/src/liboslcomp/osllex.l b/src/liboslcomp/osllex.l index d304cdea8..84b8447ee 100644 --- a/src/liboslcomp/osllex.l +++ b/src/liboslcomp/osllex.l @@ -90,6 +90,10 @@ using namespace OSL::pvt; #include "oslgram.hpp" /* Generated by bison/yacc */ +#define YY_USER_ACTION \ + oslcompiler->update_srcloc(&yyloc_param->first_line, &yyloc_param->first_column, \ + &yyloc_param->last_line, &yyloc_param->last_column, yytext); + #ifdef _WIN32 #define YY_NO_UNISTD_H @@ -128,9 +132,6 @@ namespace pvt { OSL_NAMESPACE_END #define YY_DECL int OSL::pvt::osllex(YYSTYPE* yylval_param, YYLTYPE* yyloc_param, void* yyscanner, OSLCompilerImpl* oslcompiler) -// Macro that sets the yylloc line variables to the current parse line. -#define SETLINE yyloc_param->first_line = yyloc_param->last_line = oslcompiler->lineno() - %} %% @@ -140,40 +141,38 @@ OSL_NAMESPACE_END ************************************************/ /* preprocessor symbols */ -{CPP} { OSL::pvt::preprocess (yytext, oslcompiler, yyloc_param); SETLINE; } +{CPP} { OSL::pvt::preprocess (yytext, oslcompiler, yyloc_param); } /* Comments */ -{CPLUSCOMMENT} { oslcompiler->incr_lineno(); /* skip it */ - SETLINE; - } +{CPLUSCOMMENT} { /* nothing to do, skip the comment */ } /* keywords */ -"break" { SETLINE; return (yylval->i=BREAK); } -"closure" { SETLINE; return (yylval->i=CLOSURE); } -"color" { SETLINE; return (yylval->i=COLORTYPE); } -"continue" { SETLINE; return (yylval->i=CONTINUE); } -"do" { SETLINE; return (yylval->i=DO); } -"else" { SETLINE; return (yylval->i=ELSE); } -"float" { SETLINE; return (yylval->i=FLOATTYPE); } -"for" { SETLINE; return (yylval->i=FOR); } -"if" { SETLINE; return (yylval->i=IF_TOKEN); } -"illuminance" { SETLINE; return (yylval->i=ILLUMINANCE); } -"illuminate" { SETLINE; return (yylval->i=ILLUMINATE); } -"int" { SETLINE; return (yylval->i=INTTYPE); } -"matrix" { SETLINE; return (yylval->i=MATRIXTYPE); } -"normal" { SETLINE; return (yylval->i=NORMALTYPE); } -"output" { SETLINE; return (yylval->i=OUTPUT); } -"point" { SETLINE; return (yylval->i=POINTTYPE); } -"public" { SETLINE; return (yylval->i=PUBLIC); } -"return" { SETLINE; return (yylval->i=RETURN); } -"string" { SETLINE; return (yylval->i=STRINGTYPE); } -"struct" { SETLINE; return (yylval->i=STRUCT); } -"vector" { SETLINE; return (yylval->i=VECTORTYPE); } -"void" { SETLINE; return (yylval->i=VOIDTYPE); } -"while" { SETLINE; return (yylval->i=WHILE); } -"or" { SETLINE; return (yylval->i=OR_OP); } -"and" { SETLINE; return (yylval->i=AND_OP); } -"not" { SETLINE; return (yylval->i=NOT_OP); } +"break" { return (yylval->i=BREAK); } +"closure" { return (yylval->i=CLOSURE); } +"color" { return (yylval->i=COLORTYPE); } +"continue" { return (yylval->i=CONTINUE); } +"do" { return (yylval->i=DO); } +"else" { return (yylval->i=ELSE); } +"float" { return (yylval->i=FLOATTYPE); } +"for" { return (yylval->i=FOR); } +"if" { return (yylval->i=IF_TOKEN); } +"illuminance" { return (yylval->i=ILLUMINANCE); } +"illuminate" { return (yylval->i=ILLUMINATE); } +"int" { return (yylval->i=INTTYPE); } +"matrix" { return (yylval->i=MATRIXTYPE); } +"normal" { return (yylval->i=NORMALTYPE); } +"output" { return (yylval->i=OUTPUT); } +"point" { return (yylval->i=POINTTYPE); } +"public" { return (yylval->i=PUBLIC); } +"return" { return (yylval->i=RETURN); } +"string" { return (yylval->i=STRINGTYPE); } +"struct" { return (yylval->i=STRUCT); } +"vector" { return (yylval->i=VECTORTYPE); } +"void" { return (yylval->i=VOIDTYPE); } +"while" { return (yylval->i=WHILE); } +"or" { return (yylval->i=OR_OP); } +"and" { return (yylval->i=AND_OP); } +"not" { return (yylval->i=NOT_OP); } /* reserved words */ "bool"|"case"|"char"|"class"|"const"|"default"|"double" | \ @@ -181,11 +180,9 @@ OSL_NAMESPACE_END "protected"|"short"|"signed"|"sizeof"|"static"|"struct" | \ "switch"|"template"|"this"|"true"|"typedef"|"uniform" | \ "union"|"unsigned"|"varying"|"virtual" { - oslcompiler->errorfmt(oslcompiler->filename(), - oslcompiler->lineno(), + oslcompiler->errorfmt(oslcompiler->srcloc(), "'{}' is a reserved word", yytext); - SETLINE; return (yylval->i=RESERVED); } @@ -193,7 +190,6 @@ OSL_NAMESPACE_END /* Identifiers */ {IDENT} { yylval->s = ustring(yytext).c_str(); - SETLINE; return IDENTIFIER; } @@ -203,13 +199,11 @@ OSL_NAMESPACE_END // we do not detect overflow when the value is INT_MAX+1, // because negation happens later and -(INT_MAX+1) == INT_MIN if (llval > ((long long)INT_MAX)+1) { - oslcompiler->errorfmt(oslcompiler->filename(), - oslcompiler->lineno(), + oslcompiler->errorfmt(oslcompiler->srcloc(), "integer overflow, value must be between {} and {}.", INT_MIN, INT_MAX); } yylval->i = (int)llval; - SETLINE; return INT_LITERAL; } {HEXINTEGER} { @@ -217,13 +211,11 @@ OSL_NAMESPACE_END // we do not detect overflow when the value is INT_MAX+1, // because negation happens later and -(INT_MAX+1) == INT_MIN if (llval > ((long long)UINT_MAX)+1) { - oslcompiler->errorfmt(oslcompiler->filename(), - oslcompiler->lineno(), + oslcompiler->errorfmt(oslcompiler->srcloc(), "integer overflow, value must be between {} and {}.", INT_MIN, INT_MAX); } yylval->i = (int)llval; - SETLINE; return INT_LITERAL; } @@ -231,7 +223,6 @@ OSL_NAMESPACE_END {FLT} { yylval->f = OIIO::Strutil::from_string(yytext); - SETLINE; return FLOAT_LITERAL; } @@ -245,7 +236,6 @@ OSL_NAMESPACE_END s = string_view(unescaped); } yylval->s = ustring(s).c_str(); - SETLINE; // std::cerr << "osllex string '" << yylval->s << "'\n"; return STRING_LITERAL; } @@ -254,41 +244,39 @@ OSL_NAMESPACE_END * catch-all rule, but we need to define the two-character operators * so they are not lexed as '+' and '=' separately, for example. */ -"+=" { SETLINE; return (yylval->i=ADD_ASSIGN); } -"-=" { SETLINE; return (yylval->i=SUB_ASSIGN); } -"*=" { SETLINE; return (yylval->i=MUL_ASSIGN); } -"/=" { SETLINE; return (yylval->i=DIV_ASSIGN); } -"&=" { SETLINE; return (yylval->i=BIT_AND_ASSIGN); } -"|=" { SETLINE; return (yylval->i=BIT_OR_ASSIGN); } -"^=" { SETLINE; return (yylval->i=XOR_ASSIGN); } -"<<=" { SETLINE; return (yylval->i=SHL_ASSIGN); } -">>=" { SETLINE; return (yylval->i=SHR_ASSIGN); } -"<<" { SETLINE; return (yylval->i=SHL_OP); } -">>" { SETLINE; return (yylval->i=SHR_OP); } -"&&" { SETLINE; return (yylval->i=AND_OP); } -"||" { SETLINE; return (yylval->i=OR_OP); } -"<=" { SETLINE; return (yylval->i=LE_OP); } -">=" { SETLINE; return (yylval->i=GE_OP); } -"==" { SETLINE; return (yylval->i=EQ_OP); } -"!=" { SETLINE; return (yylval->i=NE_OP); } -"++" { SETLINE; return (yylval->i=INCREMENT); } -"--" { SETLINE; return (yylval->i=DECREMENT); } +"+=" { return (yylval->i=ADD_ASSIGN); } +"-=" { return (yylval->i=SUB_ASSIGN); } +"*=" { return (yylval->i=MUL_ASSIGN); } +"/=" { return (yylval->i=DIV_ASSIGN); } +"&=" { return (yylval->i=BIT_AND_ASSIGN); } +"|=" { return (yylval->i=BIT_OR_ASSIGN); } +"^=" { return (yylval->i=XOR_ASSIGN); } +"<<=" { return (yylval->i=SHL_ASSIGN); } +">>=" { return (yylval->i=SHR_ASSIGN); } +"<<" { return (yylval->i=SHL_OP); } +">>" { return (yylval->i=SHR_OP); } +"&&" { return (yylval->i=AND_OP); } +"||" { return (yylval->i=OR_OP); } +"<=" { return (yylval->i=LE_OP); } +">=" { return (yylval->i=GE_OP); } +"==" { return (yylval->i=EQ_OP); } +"!=" { return (yylval->i=NE_OP); } +"++" { return (yylval->i=INCREMENT); } +"--" { return (yylval->i=DECREMENT); } /* Beginning of metadata */ -"[[" { SETLINE; return (yylval->i=METADATA_BEGIN); } +"[[" { return (yylval->i=METADATA_BEGIN); } /* End of line */ "\\\n" | -[\n] { oslcompiler->incr_lineno(); - SETLINE; - } +[\n] { } /* Ignore whitespace */ {WHITE} { } /* catch-all rule for any other single characters */ -! { SETLINE; return (yylval->i = NOT_OP); } -. { SETLINE; return (yylval->i = *yytext); } +! { return (yylval->i = NOT_OP); } +. { return (yylval->i = *yytext); } %% @@ -305,9 +293,8 @@ preprocess (const char *str, OSLCompilerImpl* oslcompiler, YYLTYPE* yyloc_param) while (*p == ' ' || *p == '\t') p++; if (*p != '#') { - oslcompiler->errorfmt(oslcompiler->filename(), oslcompiler->lineno(), + oslcompiler->errorfmt(oslcompiler->srcloc(), "Possible bug in shader preprocess"); - SETLINE; return; } p++; @@ -320,21 +307,17 @@ preprocess (const char *str, OSLCompilerImpl* oslcompiler, YYLTYPE* yyloc_param) if (pragmatype == "error") { string_view msg; if (OIIO::Strutil::parse_string(line, msg)) - oslcompiler->errorfmt(oslcompiler->filename(), - oslcompiler->lineno(), "{}", msg); + oslcompiler->errorfmt(oslcompiler->srcloc(), "{}", msg); else - oslcompiler->errorfmt(oslcompiler->filename(), - oslcompiler->lineno(), + oslcompiler->errorfmt(oslcompiler->srcloc(), ""); } else if (pragmatype == "warning") { string_view msg; if (OIIO::Strutil::parse_string(line, msg)) - oslcompiler->warningfmt(oslcompiler->filename(), - oslcompiler->lineno(), "{}", msg); + oslcompiler->warningfmt(oslcompiler->srcloc(), "{}", msg); else - oslcompiler->warningfmt(oslcompiler->filename(), - oslcompiler->lineno(), + oslcompiler->warningfmt(oslcompiler->srcloc(), ""); } else if (OIIO::Strutil::iequals (pragmatype, "osl")) { @@ -342,12 +325,16 @@ preprocess (const char *str, OSLCompilerImpl* oslcompiler, YYLTYPE* yyloc_param) if (pragmaname == "nowarn") { oslcompiler->pragma_nowarn (); } else { - oslcompiler->warningfmt(oslcompiler->filename(), oslcompiler->lineno(), + oslcompiler->warningfmt(oslcompiler->srcloc(), "Unknown pragma '{}'", pragmaname); } } // N.B. Any other pragmas that don't start with "osl" are ignored - oslcompiler->incr_lineno(); // the pragma ends with an EOLN + // the pragma ends with an EOLN + yyloc_param->last_line++; + yyloc_param->last_column = 0; + oslcompiler->update_srcloc(&yyloc_param->first_line, &yyloc_param->first_column, + &yyloc_param->last_line, &yyloc_param->last_column, str); } else { /* probably the line number and filename */ if (! strncmp (p, "line", 4)) p += 4; @@ -370,7 +357,7 @@ preprocess (const char *str, OSLCompilerImpl* oslcompiler, YYLTYPE* yyloc_param) filename.erase (0, 1); } ustring ufilename(filename); - oslcompiler->filename (ufilename); + oslcompiler->update_filename(ufilename); // TODO: We no longer use Boost Wave, so verify if the // workaround below is still needed .. // Spooky workaround for Boost Wave bug: force_include @@ -382,13 +369,16 @@ preprocess (const char *str, OSLCompilerImpl* oslcompiler, YYLTYPE* yyloc_param) if (ufilename == oslcompiler->main_filename()) --line; } - oslcompiler->lineno (line); + yyloc_param->last_line = line-1; + yyloc_param->last_column = 0; + oslcompiler->update_srcloc(&yyloc_param->first_line, &yyloc_param->first_column, + &yyloc_param->last_line, &yyloc_param->last_column, str); + } else { - oslcompiler->errorfmt(oslcompiler->filename(), oslcompiler->lineno(), + oslcompiler->errorfmt(oslcompiler->srcloc(), "Unrecognized preprocessor command: #{}", p); } } - SETLINE; } static std::mutex oslcompiler_mutex; diff --git a/src/liboslcomp/typecheck.cpp b/src/liboslcomp/typecheck.cpp index 919266647..e41f915c3 100644 --- a/src/liboslcomp/typecheck.cpp +++ b/src/liboslcomp/typecheck.cpp @@ -1698,8 +1698,7 @@ class CandidateFunctions { formals += advance; std::string msg = " "; if (ASTNode* decl = sym->node()) - msg += Strutil::fmt::format("{}:{}\t", decl->sourcefile(), - decl->sourceline()); + msg += Strutil::fmt::format("{}\t", decl->sourceloc()); msg += Strutil::fmt::format("{} {} ({})\n", returntype, sym->name(), TypeSpec::typelist_from_code(formals)); return msg; @@ -2034,7 +2033,7 @@ ASTfunction_call::typecheck(TypeSpec expected) } // Fix up actual arguments compound initializers that were paired with - // user function formal arguments that are unsigned arrays. They were + // user function formal arguments that are unsized arrays. They were // not yet typechecked, do so now. if (any_args_are_compound_initializers && is_user_function()) { ASTNode* formal = user_function()->formals().get(); diff --git a/src/liboslexec/loadshader.cpp b/src/liboslexec/loadshader.cpp index 0ea41a866..8b0fee169 100644 --- a/src/liboslexec/loadshader.cpp +++ b/src/liboslexec/loadshader.cpp @@ -65,8 +65,7 @@ class OSOReaderToMaster final : public OSOReader { size_t m_firstarg; ///< First argument in current op size_t m_nargs; ///< Number of args so far in current op bool m_reading_instruction; ///< Are we reading an op? - ustring m_sourcefile; ///< Current source file parsed - int m_sourceline; ///< Current source code line parsed + SrcLoc m_srcloc; ///< Current source code location parsed ustring m_codesection; ///< Which entry point are the ops for? int m_codesym; ///< Which param is being initialized? int m_oso_major, m_oso_minor; ///< oso file format version @@ -320,11 +319,25 @@ OSOReaderToMaster::hint(string_view hintstring) string_view h(hintstring); if (Strutil::parse_prefix(h, "%filename{\"")) { - m_sourcefile = Strutil::parse_until(h, "\""); + m_srcloc.filename = Strutil::parse_until(h, "\""); return; } if (Strutil::parse_prefix(h, "%line{")) { - Strutil::parse_int(h, m_sourceline); + int l = 0; + Strutil::parse_int(h, l); + m_srcloc.line_start = l - 1; // line_start is 0-based + return; + } + if (Strutil::parse_prefix(h, "%srcloc{")) { + int fl = 0, fc = 0, ll = 0, lc = 0; + Strutil::parse_int(h, fl); + Strutil::parse_char(h, ','); // skip the separator + Strutil::parse_int(h, fc); + Strutil::parse_char(h, ','); // skip the separator + Strutil::parse_int(h, ll); + Strutil::parse_char(h, ','); // skip the separator + Strutil::parse_int(h, lc); + m_srcloc.from_yyloc(fl, fc, ll, lc); return; } if (Strutil::parse_prefix(h, "%structfields{") @@ -370,10 +383,13 @@ OSOReaderToMaster::hint(string_view hintstring) string_view str = Strutil::parse_until(h, "}"); Strutil::parse_string(str, str, false, Strutil::DeleteQuotes); if (str.size() != m_nargs) { + // Note that here we report the line number in the OSO, not the + // original OSL source, as that's where we found the problem m_shadingsys.errorfmt( - "Parsing shader {}: malformed hint '{}' on op {} line {}", + "Parsing shader {}: malformed hint '{}' on op {} at OSO line {}", m_master->shadername(), hintstring, - m_master->m_ops.back().opname(), m_sourceline); + m_master->m_ops.back().opname(), + lineno()); m_errors = true; } for (size_t i = 0; str.size() && i < m_nargs; @@ -444,7 +460,7 @@ OSOReaderToMaster::hint(string_view hintstring) void OSOReaderToMaster::codemarker(const char* name) { - m_sourcefile.clear(); + m_srcloc.filename.clear(); int nextop = (int)m_master->m_ops.size(); codeend(); // Mark the end spot, if we were parsing ops before @@ -541,7 +557,7 @@ void OSOReaderToMaster::instruction_end() { m_master->m_ops.back().set_args(m_firstarg, m_nargs); - m_master->m_ops.back().source(m_sourcefile, m_sourceline); + m_master->m_ops.back().source(m_srcloc); m_reading_instruction = false; } diff --git a/src/liboslexec/runtimeoptimize.cpp b/src/liboslexec/runtimeoptimize.cpp index 6ef2b74af..79ce5028d 100644 --- a/src/liboslexec/runtimeoptimize.cpp +++ b/src/liboslexec/runtimeoptimize.cpp @@ -551,8 +551,7 @@ RuntimeOptimizer::insert_code(int opnum, ustring opname, if ((relation == -1 && opnum > 0) || (relation == 1 && opnum < (int)code.size() - 1)) { code[opnum].method(code[opnum + relation].method()); - code[opnum].source(code[opnum + relation].sourcefile(), - code[opnum + relation].sourceline()); + code[opnum].source(code[opnum + relation].srcloc()); } // Unless we were inserting at the end, we may need to adjust @@ -668,8 +667,7 @@ RuntimeOptimizer::insert_useparam(size_t opnum, if (opnum < code.size() - 1) { // We have no parse node, but we set the new instruction's // "source" to the one of the statement right after. - code[opnum].source(code[opnum + 1].sourcefile(), - code[opnum + 1].sourceline()); + code[opnum].source(code[opnum + 1].srcloc()); // Set the method id to the same as the statement right after code[opnum].method(code[opnum + 1].method()); } else {