Added line numbers to DSLParser errors

Change-Id: I895c3d94c9a49cc0350b287d3aa722050b91295e
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/443036
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
diff --git a/include/sksl/DSLCore.h b/include/sksl/DSLCore.h
index 8bbbb21..d6ebf85 100644
--- a/include/sksl/DSLCore.h
+++ b/include/sksl/DSLCore.h
@@ -78,12 +78,12 @@
 /**
  * break;
  */
-DSLStatement Break();
+DSLStatement Break(PositionInfo pos = PositionInfo::Capture());
 
 /**
  * continue;
  */
-DSLStatement Continue();
+DSLStatement Continue(PositionInfo pos = PositionInfo::Capture());
 
 /**
  * Creates a local variable declaration statement.
diff --git a/include/sksl/DSLExpression.h b/include/sksl/DSLExpression.h
index 837c5b4..d7ed8ea 100644
--- a/include/sksl/DSLExpression.h
+++ b/include/sksl/DSLExpression.h
@@ -43,45 +43,47 @@
     /**
      * Creates an expression representing a literal float.
      */
-    DSLExpression(float value);
+    DSLExpression(float value, PositionInfo pos = PositionInfo::Capture());
 
     /**
      * Creates an expression representing a literal float.
      */
-    DSLExpression(double value)
+    DSLExpression(double value, PositionInfo pos = PositionInfo::Capture())
         : DSLExpression((float) value) {}
 
     /**
      * Creates an expression representing a literal int.
      */
-    DSLExpression(int value);
+    DSLExpression(int value, PositionInfo pos = PositionInfo::Capture());
 
     /**
      * Creates an expression representing a literal int.
      */
-    DSLExpression(int64_t value);
+    DSLExpression(int64_t value, PositionInfo pos = PositionInfo::Capture());
 
     /**
      * Creates an expression representing a literal uint.
      */
-    DSLExpression(unsigned int value);
+    DSLExpression(unsigned int value, PositionInfo pos = PositionInfo::Capture());
 
     /**
      * Creates an expression representing a literal bool.
      */
-    DSLExpression(bool value);
+    DSLExpression(bool value, PositionInfo pos = PositionInfo::Capture());
 
     /**
      * Creates an expression representing a variable reference.
      */
-    DSLExpression(DSLVarBase& var);
+    DSLExpression(DSLVarBase& var, PositionInfo pos = PositionInfo::Capture());
 
-    DSLExpression(DSLVarBase&& var);
+    DSLExpression(DSLVarBase&& var, PositionInfo pos = PositionInfo::Capture());
 
     DSLExpression(DSLPossibleExpression expr, PositionInfo pos = PositionInfo::Capture());
 
     explicit DSLExpression(std::unique_ptr<SkSL::Expression> expression);
 
+    static DSLExpression Poison(PositionInfo pos = PositionInfo::Capture());
+
     ~DSLExpression();
 
     DSLType type();
@@ -117,7 +119,8 @@
      */
     DSLPossibleExpression operator[](DSLExpression index);
 
-    DSLPossibleExpression operator()(SkTArray<DSLWrapper<DSLExpression>> args);
+    DSLPossibleExpression operator()(SkTArray<DSLWrapper<DSLExpression>> args,
+                                     PositionInfo pos = PositionInfo::Capture());
 
     /**
      * Returns true if this object contains an expression. DSLExpressions which were created with
@@ -258,7 +261,8 @@
 
     DSLPossibleExpression operator[](DSLExpression index);
 
-    DSLPossibleExpression operator()(SkTArray<DSLWrapper<DSLExpression>> args);
+    DSLPossibleExpression operator()(SkTArray<DSLWrapper<DSLExpression>> args,
+                                     PositionInfo pos = PositionInfo::Capture());
 
     DSLPossibleExpression operator++();
 
diff --git a/include/sksl/DSLFunction.h b/include/sksl/DSLFunction.h
index 475a791..452d743 100644
--- a/include/sksl/DSLFunction.h
+++ b/include/sksl/DSLFunction.h
@@ -40,17 +40,19 @@
         // (parameterArray.push_back(&parameters), ...);
         int unused[] = {0, (static_cast<void>(parameterArray.push_back(&parameters)), 0)...};
         static_cast<void>(unused);
-        this->init(modifiers, returnType, name, std::move(parameterArray));
+        // We can't have a default parameter and a template parameter pack at the same time, so
+        // unfortunately we can't capture position info from this overload.
+        this->init(modifiers, returnType, name, std::move(parameterArray), PositionInfo());
     }
 
     DSLFunction(const DSLType& returnType, skstd::string_view name,
-                SkTArray<DSLParameter*> parameters) {
-        this->init(DSLModifiers(), returnType, name, std::move(parameters));
+                SkTArray<DSLParameter*> parameters, PositionInfo pos = PositionInfo::Capture()) {
+        this->init(DSLModifiers(), returnType, name, std::move(parameters), pos);
     }
 
     DSLFunction(const DSLModifiers& modifiers, const DSLType& returnType, skstd::string_view name,
-                SkTArray<DSLParameter*> parameters) {
-        this->init(modifiers, returnType, name, std::move(parameters));
+                SkTArray<DSLParameter*> parameters, PositionInfo pos = PositionInfo::Capture()) {
+        this->init(modifiers, returnType, name, std::move(parameters), pos);
     }
 
     DSLFunction(const SkSL::FunctionDeclaration* decl)
@@ -100,7 +102,7 @@
     }
 
     void init(DSLModifiers modifiers, const DSLType& returnType, skstd::string_view name,
-              SkTArray<DSLParameter*> params);
+              SkTArray<DSLParameter*> params, PositionInfo pos);
 
     const SkSL::FunctionDeclaration* fDecl = nullptr;
 };
diff --git a/include/sksl/DSLModifiers.h b/include/sksl/DSLModifiers.h
index 66d622b..e0a9689 100644
--- a/include/sksl/DSLModifiers.h
+++ b/include/sksl/DSLModifiers.h
@@ -49,7 +49,7 @@
 private:
     SkSL::Modifiers fModifiers;
 
-    friend DSLType Struct(skstd::string_view name, SkSpan<DSLField> fields);
+    friend DSLType Struct(skstd::string_view name, SkSpan<DSLField> fields, PositionInfo pos);
     friend class DSLCore;
     friend class DSLFunction;
     friend class DSLType;
diff --git a/include/sksl/DSLSymbols.h b/include/sksl/DSLSymbols.h
index 0281ba2..93e32ad 100644
--- a/include/sksl/DSLSymbols.h
+++ b/include/sksl/DSLSymbols.h
@@ -47,7 +47,7 @@
 /**
  * Returns an expression referring to the named symbol.
  */
-DSLPossibleExpression Symbol(skstd::string_view name);
+DSLPossibleExpression Symbol(skstd::string_view name, PositionInfo pos = PositionInfo::Capture());
 
 /**
  * Returns true if the name refers to a type.
diff --git a/include/sksl/DSLType.h b/include/sksl/DSLType.h
index 73c601c..e8f2e2b 100644
--- a/include/sksl/DSLType.h
+++ b/include/sksl/DSLType.h
@@ -166,8 +166,8 @@
 
     TypeConstant fTypeConstant = kPoison_Type;
 
-    friend DSLType Array(const DSLType& base, int count);
-    friend DSLType Struct(skstd::string_view name, SkSpan<DSLField> fields);
+    friend DSLType Array(const DSLType& base, int count, PositionInfo pos);
+    friend DSLType Struct(skstd::string_view name, SkSpan<DSLField> fields, PositionInfo pos);
     friend class DSLCore;
     friend class DSLFunction;
     friend class DSLVarBase;
@@ -212,7 +212,7 @@
 #undef VECTOR_TYPE
 #undef MATRIX_TYPE
 
-DSLType Array(const DSLType& base, int count);
+DSLType Array(const DSLType& base, int count, PositionInfo pos = PositionInfo::Capture());
 
 class DSLField {
 public:
@@ -230,15 +230,16 @@
     skstd::string_view fName;
 
     friend class DSLCore;
-    friend DSLType Struct(skstd::string_view name, SkSpan<DSLField> fields);
+    friend DSLType Struct(skstd::string_view name, SkSpan<DSLField> fields, PositionInfo pos);
 };
 
-DSLType Struct(skstd::string_view name, SkSpan<DSLField> fields);
+DSLType Struct(skstd::string_view name, SkSpan<DSLField> fields,
+               PositionInfo pos = PositionInfo::Capture());
 
 template<typename... Field>
 DSLType Struct(skstd::string_view name, Field... fields) {
     DSLField fieldTypes[] = {std::move(fields)...};
-    return Struct(name, SkMakeSpan(fieldTypes));
+    return Struct(name, SkMakeSpan(fieldTypes), PositionInfo());
 }
 
 } // namespace dsl
diff --git a/include/sksl/DSLVar.h b/include/sksl/DSLVar.h
index 1b9250b..cfac770 100644
--- a/include/sksl/DSLVar.h
+++ b/include/sksl/DSLVar.h
@@ -34,14 +34,15 @@
      * name conflicts and the variable's name is only important when debugging shaders, the name
      * parameter is optional.
      */
-    DSLVarBase(DSLType type, skstd::string_view name, DSLExpression initialValue);
+    DSLVarBase(DSLType type, skstd::string_view name, DSLExpression initialValue, PositionInfo pos);
 
-    DSLVarBase(DSLType type, DSLExpression initialValue);
+    DSLVarBase(DSLType type, DSLExpression initialValue, PositionInfo pos);
 
     DSLVarBase(const DSLModifiers& modifiers, DSLType type, skstd::string_view name,
-               DSLExpression initialValue);
+               DSLExpression initialValue, PositionInfo pos);
 
-    DSLVarBase(const DSLModifiers& modifiers, DSLType type, DSLExpression initialValue);
+    DSLVarBase(const DSLModifiers& modifiers, DSLType type, DSLExpression initialValue,
+               PositionInfo pos);
 
     DSLVarBase(DSLVarBase&&) = default;
 
@@ -131,6 +132,7 @@
     // true if we have attempted to create the SkSL var
     bool fInitialized = false;
     bool fDeclared = false;
+    PositionInfo fPosition;
 
     friend class DSLCore;
     friend class DSLExpression;
@@ -148,22 +150,24 @@
     DSLVar() = default;
 
     DSLVar(DSLType type, skstd::string_view name = "var",
-           DSLExpression initialValue = DSLExpression())
-        : INHERITED(type, name, std::move(initialValue)) {}
+           DSLExpression initialValue = DSLExpression(),
+           PositionInfo pos = PositionInfo::Capture())
+        : INHERITED(type, name, std::move(initialValue), pos) {}
 
-    DSLVar(DSLType type, const char* name, DSLExpression initialValue = DSLExpression())
-        : DSLVar(type, skstd::string_view(name), std::move(initialValue)) {}
+    DSLVar(DSLType type, const char* name, DSLExpression initialValue = DSLExpression(),
+           PositionInfo pos = PositionInfo::Capture())
+        : DSLVar(type, skstd::string_view(name), std::move(initialValue), pos) {}
 
-    DSLVar(DSLType type, DSLExpression initialValue)
-        : INHERITED(type, std::move(initialValue)) {}
+    DSLVar(DSLType type, DSLExpression initialValue, PositionInfo pos = PositionInfo::Capture())
+        : INHERITED(type, std::move(initialValue), pos) {}
 
     DSLVar(const DSLModifiers& modifiers, DSLType type, skstd::string_view name = "var",
-           DSLExpression initialValue = DSLExpression())
-        : INHERITED(modifiers, type, name, std::move(initialValue)) {}
+           DSLExpression initialValue = DSLExpression(), PositionInfo pos = PositionInfo::Capture())
+        : INHERITED(modifiers, type, name, std::move(initialValue), pos) {}
 
     DSLVar(const DSLModifiers& modifiers, DSLType type, const char* name,
-           DSLExpression initialValue = DSLExpression())
-        : DSLVar(modifiers, type, skstd::string_view(name), std::move(initialValue)) {}
+           DSLExpression initialValue = DSLExpression(), PositionInfo pos = PositionInfo::Capture())
+        : DSLVar(modifiers, type, skstd::string_view(name), std::move(initialValue), pos) {}
 
     DSLVar(DSLVar&&) = default;
 
@@ -194,22 +198,24 @@
     DSLGlobalVar() = default;
 
     DSLGlobalVar(DSLType type, skstd::string_view name = "var",
-           DSLExpression initialValue = DSLExpression())
-        : INHERITED(type, name, std::move(initialValue)) {}
+           DSLExpression initialValue = DSLExpression(), PositionInfo pos = PositionInfo::Capture())
+        : INHERITED(type, name, std::move(initialValue), pos) {}
 
-    DSLGlobalVar(DSLType type, const char* name, DSLExpression initialValue = DSLExpression())
-        : DSLGlobalVar(type, skstd::string_view(name), std::move(initialValue)) {}
+    DSLGlobalVar(DSLType type, const char* name, DSLExpression initialValue = DSLExpression(),
+                 PositionInfo pos = PositionInfo::Capture())
+        : DSLGlobalVar(type, skstd::string_view(name), std::move(initialValue), pos) {}
 
-    DSLGlobalVar(DSLType type, DSLExpression initialValue)
-        : INHERITED(type, std::move(initialValue)) {}
+    DSLGlobalVar(DSLType type, DSLExpression initialValue,
+                 PositionInfo pos = PositionInfo::Capture())
+        : INHERITED(type, std::move(initialValue), pos) {}
 
     DSLGlobalVar(const DSLModifiers& modifiers, DSLType type, skstd::string_view name = "var",
-           DSLExpression initialValue = DSLExpression())
-        : INHERITED(modifiers, type, name, std::move(initialValue)) {}
+           DSLExpression initialValue = DSLExpression(), PositionInfo pos = PositionInfo::Capture())
+        : INHERITED(modifiers, type, name, std::move(initialValue), pos) {}
 
     DSLGlobalVar(const DSLModifiers& modifiers, DSLType type, const char* name,
-           DSLExpression initialValue = DSLExpression())
-        : DSLGlobalVar(modifiers, type, skstd::string_view(name), std::move(initialValue)) {}
+           DSLExpression initialValue = DSLExpression(), PositionInfo pos = PositionInfo::Capture())
+        : DSLGlobalVar(modifiers, type, skstd::string_view(name), std::move(initialValue), pos) {}
 
     DSLGlobalVar(const char* name);
 
@@ -241,17 +247,20 @@
 public:
     DSLParameter() = default;
 
-    DSLParameter(DSLType type, skstd::string_view name = "var")
-        : INHERITED(type, name, DSLExpression()) {}
+    DSLParameter(DSLType type, skstd::string_view name = "var",
+                 PositionInfo pos = PositionInfo::Capture())
+        : INHERITED(type, name, DSLExpression(), pos) {}
 
-    DSLParameter(DSLType type, const char* name)
-        : DSLParameter(type, skstd::string_view(name)) {}
+    DSLParameter(DSLType type, const char* name, PositionInfo pos = PositionInfo::Capture())
+        : DSLParameter(type, skstd::string_view(name), pos) {}
 
-    DSLParameter(const DSLModifiers& modifiers, DSLType type, skstd::string_view name = "var")
-        : INHERITED(modifiers, type, name, DSLExpression()) {}
+    DSLParameter(const DSLModifiers& modifiers, DSLType type, skstd::string_view name = "var",
+                 PositionInfo pos = PositionInfo::Capture())
+        : INHERITED(modifiers, type, name, DSLExpression(), pos) {}
 
-    DSLParameter(const DSLModifiers& modifiers, DSLType type, const char* name)
-        : DSLParameter(modifiers, type, skstd::string_view(name)) {}
+    DSLParameter(const DSLModifiers& modifiers, DSLType type, const char* name,
+                 PositionInfo pos = PositionInfo::Capture())
+        : DSLParameter(modifiers, type, skstd::string_view(name), pos) {}
 
     DSLParameter(DSLParameter&&) = default;
 
diff --git a/include/sksl/SkSLErrorReporter.h b/include/sksl/SkSLErrorReporter.h
index 5ee35e5..4a3de92 100644
--- a/include/sksl/SkSLErrorReporter.h
+++ b/include/sksl/SkSLErrorReporter.h
@@ -33,17 +33,42 @@
     static PositionInfo Capture() { return PositionInfo(); }
 #endif // __has_builtin(__builtin_FILE) && __has_builtin(__builtin_LINE)
 
+    static PositionInfo Offset(const char* file, const char* text, int offset) {
+        PositionInfo result(file, -1);
+        result.fText = text;
+        result.fOffset = offset;
+        return result;
+    }
+
     const char* file_name() const {
         return fFile;
     }
 
     int line() {
+        if (fLine == -1) {
+            if (fOffset == -1 || !fText) {
+                return -1;
+            }
+            fLine = 1;
+            for (int i = 0; i < fOffset; i++) {
+                SkASSERT(fText[i]);
+                if (fText[i] == '\n') {
+                    ++fLine;
+                }
+            }
+        }
         return fLine;
     }
 
+    int offset() {
+        return fOffset;
+    }
+
 private:
-    const char* fFile;
-    int fLine;
+    const char* fFile = nullptr;
+    const char* fText = nullptr;
+    int32_t fOffset = -1;
+    int32_t fLine = -1;
 };
 
 /**
diff --git a/src/sksl/SkSLCompiler.cpp b/src/sksl/SkSLCompiler.cpp
index ab3bc8e..602f755 100644
--- a/src/sksl/SkSLCompiler.cpp
+++ b/src/sksl/SkSLCompiler.cpp
@@ -990,10 +990,6 @@
 #endif // defined(SKSL_STANDALONE) || SK_SUPPORT_GPU
 
 void Compiler::handleError(const char* msg, PositionInfo pos) {
-    if (strstr(msg, POISON_TAG)) {
-        // don't report errors on poison values
-        return;
-    }
     fErrorText += "error: " + (pos.line() >= 1 ? to_string(pos.line()) + ": " : "") + msg + "\n";
 }
 
diff --git a/src/sksl/SkSLCompiler.h b/src/sksl/SkSLCompiler.h
index b50fd80..deb772b 100644
--- a/src/sksl/SkSLCompiler.h
+++ b/src/sksl/SkSLCompiler.h
@@ -276,6 +276,7 @@
 
     friend class AutoSource;
     friend class ::SkSLCompileBench;
+    friend class DSLParser;
     friend class dsl::DSLCore;
     friend class dsl::DSLWriter;
 };
diff --git a/src/sksl/SkSLDSLParser.cpp b/src/sksl/SkSLDSLParser.cpp
index 55320a5..c4477d6 100644
--- a/src/sksl/SkSLDSLParser.cpp
+++ b/src/sksl/SkSLDSLParser.cpp
@@ -100,14 +100,13 @@
                      String text)
     : fCompiler(*compiler)
     , fSettings(settings)
-    , fErrorReporter(&compiler->errorReporter())
     , fKind(kind)
-    , fText(std::move(text))
+    , fText(std::make_unique<String>(std::move(text)))
     , fPushback(Token::Kind::TK_NONE, -1, -1) {
     // We don't want to have to worry about manually releasing all of the objects in the event that
     // an error occurs
     fSettings.fAssertDSLObjectsReleased = false;
-    fLexer.start(fText);
+    fLexer.start(*fText);
     static const bool layoutMapInitialized = []{ InitLayoutMap(); return true; }();
     (void) layoutMapInitialized;
 }
@@ -187,7 +186,15 @@
 }
 
 skstd::string_view DSLParser::text(Token token) {
-    return skstd::string_view(fText.data() + token.fOffset, token.fLength);
+    return skstd::string_view(fText->data() + token.fOffset, token.fLength);
+}
+
+PositionInfo DSLParser::position(Token t) {
+    return this->position(t.fOffset);
+}
+
+PositionInfo DSLParser::position(int offset) {
+    return PositionInfo::Offset("<unknown>", fText->c_str(), offset);
 }
 
 void DSLParser::error(Token token, String msg) {
@@ -195,7 +202,7 @@
 }
 
 void DSLParser::error(int offset, String msg) {
-    GetErrorReporter().error(msg.c_str(), PositionInfo());
+    GetErrorReporter().error(msg.c_str(), this->position(offset));
 }
 
 /* declaration* END_OF_FILE */
@@ -203,6 +210,7 @@
     ErrorReporter* errorReporter = &fCompiler.errorReporter();
     Start(&fCompiler, fKind, fSettings);
     SetErrorReporter(errorReporter);
+    errorReporter->setSource(fText->c_str());
     fEncounteredFatalError = false;
     std::unique_ptr<Program> result;
     bool done = false;
@@ -211,12 +219,12 @@
             case Token::Kind::TK_END_OF_FILE:
                 done = true;
                 if (!errorReporter->errorCount()) {
-                    result = dsl::ReleaseProgram(std::make_unique<String>(std::move(fText)));
+                    result = dsl::ReleaseProgram(std::move(fText));
                 }
                 break;
             case Token::Kind::TK_INVALID: {
+                this->nextToken();
                 this->error(this->peek(), String("invalid token"));
-                done = true;
                 break;
             }
             default:
@@ -225,6 +233,7 @@
         }
     }
     End();
+    errorReporter->setSource(nullptr);
     return result;
 }
 
@@ -234,6 +243,7 @@
     Token lookahead = this->peek();
     switch (lookahead.fKind) {
         case Token::Kind::TK_SEMICOLON:
+            this->nextToken();
             this->error(lookahead.fOffset, "expected a declaration, but found ';'");
             return false;
         default:
@@ -264,7 +274,8 @@
     if (this->checkNext(Token::Kind::TK_LPAREN)) {
         return this->functionDeclarationEnd(modifiers, *type, name);
     } else {
-        SkTArray<DSLGlobalVar> result = this->varDeclarationEnd<DSLGlobalVar>(modifiers, *type,
+        SkTArray<DSLGlobalVar> result = this->varDeclarationEnd<DSLGlobalVar>(this->position(name),
+                                                                              modifiers, *type,
                                                                               this->text(name));
         Declare(result);
         return true;
@@ -301,7 +312,7 @@
     for (DSLWrapper<DSLParameter>& param : parameters) {
         parameterPointers.push_back(&param.get());
     }
-    DSLFunction result(modifiers, type, this->text(name), parameterPointers);
+    DSLFunction result(modifiers, type, this->text(name), parameterPointers, this->position(name));
     if (!this->checkNext(Token::Kind::TK_SEMICOLON)) {
         AutoDSLSymbolTable symbols;
         for (DSLParameter* var : parameterPointers) {
@@ -324,26 +335,45 @@
     return Declare(vars);
 }
 
+SKSL_INT DSLParser::arraySize() {
+    Token next = this->peek();
+    if (next.fKind == Token::Kind::TK_INT_LITERAL) {
+        SKSL_INT size;
+        if (this->intLiteral(&size)) {
+            if (size > INT32_MAX) {
+                this->error(next, "array size out of bounds");
+                return 1;
+            }
+            if (size <= 0) {
+                this->error(next, "array size must be positive");
+                return 1;
+            }
+            return size;
+        }
+        return 1;
+    } else if (this->checkNext(Token::Kind::TK_MINUS) &&
+               this->checkNext(Token::Kind::TK_INT_LITERAL)) {
+        this->error(next, "array size must be positive");
+        return 1;
+    } else {
+        this->expression();
+        this->error(next, "expected int literal");
+        return 1;
+    }
+}
+
 template<class T>
-SkTArray<T> DSLParser::varDeclarationEnd(const dsl::DSLModifiers& mods, dsl::DSLType baseType,
-                                         skstd::string_view name) {
+SkTArray<T> DSLParser::varDeclarationEnd(PositionInfo pos, const dsl::DSLModifiers& mods,
+                                         dsl::DSLType baseType, skstd::string_view name) {
     using namespace dsl;
     SkTArray<T> result;
     int offset = this->peek().fOffset;
     auto parseArrayDimensions = [&](DSLType* type) -> bool {
         while (this->checkNext(Token::Kind::TK_LBRACKET)) {
-            if (type->isArray()) {
-                this->error(this->peek(), "multi-dimensional arrays are not supported");
-                return {};
-            }
             if (this->checkNext(Token::Kind::TK_RBRACKET)) {
                 this->error(offset, "expected array dimension");
             } else {
-                SKSL_INT size;
-                if (!this->intLiteral(&size)) {
-                    return {};
-                }
-                *type = Array(*type, size);
+                *type = Array(*type, this->arraySize(), pos);
                 if (!this->expect(Token::Kind::TK_RBRACKET, "']'")) {
                     return {};
                 }
@@ -367,10 +397,8 @@
     if (!parseArrayDimensions(&type)) {
         return {};
     }
-    if (!parseInitializer(&initializer)) {
-        return {};
-    }
-    result.push_back(T(mods, type, name, std::move(initializer)));
+    parseInitializer(&initializer);
+    result.push_back(T(mods, type, name, std::move(initializer), pos));
     AddToSymbolTable(result.back());
 
     while (this->checkNext(Token::Kind::TK_COMMA)) {
@@ -414,9 +442,10 @@
         VarDeclarationsPrefix prefix;
         if (this->varDeclarationsPrefix(&prefix)) {
             checkpoint.accept();
-            return declaration_statements(this->varDeclarationEnd<DSLVar>(prefix.modifiers,
-                                                                          prefix.type,
-                                                                          this->text(prefix.name)),
+            return declaration_statements(this->varDeclarationEnd<DSLVar>(prefix.fPosition,
+                                                                          prefix.fModifiers,
+                                                                          prefix.fType,
+                                                                          this->text(prefix.fName)),
                                           this->symbols());
         }
 
@@ -430,13 +459,14 @@
 // Helper function for varDeclarations(). If this function succeeds, we assume that the rest of the
 // statement is a variable-declaration statement, not an expression-statement.
 bool DSLParser::varDeclarationsPrefix(VarDeclarationsPrefix* prefixData) {
-    prefixData->modifiers = this->modifiers();
-    skstd::optional<DSLType> type = this->type(prefixData->modifiers);
+    prefixData->fPosition = this->position(this->peek());
+    prefixData->fModifiers = this->modifiers();
+    skstd::optional<DSLType> type = this->type(prefixData->fModifiers);
     if (!type) {
         return false;
     }
-    prefixData->type = *type;
-    return this->expectIdentifier(&prefixData->name);
+    prefixData->fType = *type;
+    return this->expectIdentifier(&prefixData->fName);
 }
 
 /* modifiers type IDENTIFIER varDeclarationEnd */
@@ -445,8 +475,10 @@
     if (!this->varDeclarationsPrefix(&prefix)) {
         return skstd::nullopt;
     }
-    return declaration_statements(this->varDeclarationEnd<DSLVar>(prefix.modifiers, prefix.type,
-                                                                  this->text(prefix.name)),
+    return declaration_statements(this->varDeclarationEnd<DSLVar>(prefix.fPosition,
+                                                                  prefix.fModifiers,
+                                                                  prefix.fType,
+                                                                  this->text(prefix.fName)),
                                   this->symbols());
 }
 
@@ -486,18 +518,8 @@
                 return skstd::nullopt;
             }
 
-            if (this->checkNext(Token::Kind::TK_LBRACKET)) {
-                SKSL_INT arraySize;
-                Token sizeToken = this->peek();
-                if (!this->DSLParser::intLiteral(&arraySize)) {
-                    this->error(sizeToken.fOffset, "expected an integer array size");
-                    return skstd::nullopt;
-                }
-                if (arraySize <= 0 || arraySize > INT_MAX) {
-                    this->error(sizeToken.fOffset, "array size is invalid");
-                    return skstd::nullopt;
-                }
-                actualType = dsl::Array(actualType, arraySize);
+            while (this->checkNext(Token::Kind::TK_LBRACKET)) {
+                actualType = dsl::Array(actualType, this->arraySize(), this->position(memberName));
                 if (!this->expect(Token::Kind::TK_RBRACKET, "']'")) {
                     return skstd::nullopt;
                 }
@@ -513,18 +535,20 @@
                     "struct '" + this->text(name) + "' must contain at least one field");
         return skstd::nullopt;
     }
-    return dsl::Struct(this->text(name), SkMakeSpan(fields));
+    return dsl::Struct(this->text(name), SkMakeSpan(fields), this->position(name));
 }
 
 /* structDeclaration ((IDENTIFIER varDeclarationEnd) | SEMICOLON) */
 SkTArray<dsl::DSLGlobalVar> DSLParser::structVarDeclaration(const DSLModifiers& modifiers) {
+    PositionInfo pos = this->position(this->peek());
     skstd::optional<DSLType> type = this->structDeclaration();
     if (!type) {
         return {};
     }
     Token name;
     if (this->checkNext(Token::Kind::TK_IDENTIFIER, &name)) {
-        return this->varDeclarationEnd<DSLGlobalVar>(modifiers, std::move(*type), this->text(name));
+        return this->varDeclarationEnd<DSLGlobalVar>(pos, modifiers, std::move(*type),
+                                                     this->text(name));
     }
     this->expect(Token::Kind::TK_SEMICOLON, "';'");
     return {};
@@ -542,10 +566,6 @@
         return skstd::nullopt;
     }
     while (this->checkNext(Token::Kind::TK_LBRACKET)) {
-        if (type->isArray()) {
-            this->error(this->peek(), "multi-dimensional arrays are not supported");
-            return skstd::nullopt;
-        }
         Token sizeToken;
         if (!this->expect(Token::Kind::TK_INT_LITERAL, "a positive integer", &sizeToken)) {
             return skstd::nullopt;
@@ -556,12 +576,12 @@
             this->error(sizeToken, "array size is too large: " + arraySizeFrag);
             return skstd::nullopt;
         }
-        type = Array(*type, arraySize);
+        type = Array(*type, arraySize, this->position(name));
         if (!this->expect(Token::Kind::TK_RBRACKET, "']'")) {
             return skstd::nullopt;
         }
     }
-    return {{DSLParameter(modifiers, *type, this->text(name))}};
+    return {{DSLParameter(modifiers, *type, this->text(name), this->position(name))}};
 }
 
 /** EQ INT_LITERAL */
@@ -743,17 +763,8 @@
     }
     DSLType result(this->text(type), modifiers);
     while (this->checkNext(Token::Kind::TK_LBRACKET)) {
-        if (result.isArray()) {
-            this->error(this->peek(), "multi-dimensional arrays are not supported");
-            return skstd::nullopt;
-        }
         if (this->peek().fKind != Token::Kind::TK_RBRACKET) {
-            SKSL_INT i;
-            if (this->intLiteral(&i)) {
-                result = Array(result,  i);
-            } else {
-                return skstd::nullopt;
-            }
+            result = Array(result, this->arraySize(), this->position(type));
         } else {
             this->error(this->peek(), "expected array dimension");
         }
@@ -794,15 +805,8 @@
             if (this->checkNext(Token::Kind::TK_LBRACKET)) {
                 Token sizeToken = this->peek();
                 if (sizeToken.fKind != Token::Kind::TK_RBRACKET) {
-                    SKSL_INT arraySize = 0;
-                    if (this->DSLParser::intLiteral(&arraySize)) {
-                        if (arraySize <= 0) {
-                            this->error(sizeToken, "array size must be positive");
-                        }
-                        actualType = Array(std::move(actualType), arraySize);
-                    } else {
-                        return false;
-                    }
+                    actualType = Array(std::move(actualType), this->arraySize(),
+                                       this->position(typeName));
                 } else {
                     this->error(sizeToken, "unsized arrays are not permitted");
                 }
@@ -826,19 +830,7 @@
     if (this->checkNext(Token::Kind::TK_IDENTIFIER, &instanceNameToken)) {
         instanceName = this->text(instanceNameToken);
         if (this->checkNext(Token::Kind::TK_LBRACKET)) {
-            Token sizeToken = this->peek();
-            if (sizeToken.fKind != Token::Kind::TK_RBRACKET) {
-                if (this->DSLParser::intLiteral(&arraySize)) {
-                    if (arraySize <= 0) {
-                        this->error(sizeToken, "array size must be positive");
-                    }
-                } else {
-                    return false;
-                }
-            } else {
-                this->error(sizeToken, "unsized arrays are not permitted");
-                return false;
-            }
+            arraySize = this->arraySize();
             this->expect(Token::Kind::TK_RBRACKET, "']'");
         }
     }
@@ -880,10 +872,10 @@
     }
     if (isStatic) {
         return StaticIf(std::move(**test), std::move(*ifTrue),
-                        ifFalse ? std::move(*ifFalse) : DSLStatement());
+                        ifFalse ? std::move(*ifFalse) : DSLStatement(), this->position(start));
     } else {
         return If(std::move(**test), std::move(*ifTrue),
-                  ifFalse ? std::move(*ifFalse) : DSLStatement());
+                  ifFalse ? std::move(*ifFalse) : DSLStatement(), this->position(start));
     }
 }
 
@@ -1102,7 +1094,7 @@
     if (!this->expect(Token::Kind::TK_SEMICOLON, "';'")) {
         return skstd::nullopt;
     }
-    return Break();
+    return Break(this->position(start));
 }
 
 /* CONTINUE SEMICOLON */
@@ -1114,7 +1106,7 @@
     if (!this->expect(Token::Kind::TK_SEMICOLON, "';'")) {
         return skstd::nullopt;
     }
-    return Continue();
+    return Continue(this->position(start));
 }
 
 /* DISCARD SEMICOLON */
@@ -1503,7 +1495,7 @@
                                                               skstd::string_view swizzleMask) {
     SkASSERT(swizzleMask.length() > 0);
     if (!base.type().isVector() && !base.type().isScalar()) {
-        return base.field(swizzleMask);
+        return base.field(swizzleMask, this->position(offset));
     }
     int length = swizzleMask.length();
     if (length > 4) {
@@ -1549,7 +1541,7 @@
 
 skstd::optional<dsl::Wrapper<dsl::DSLExpression>> DSLParser::call(int offset,
         dsl::DSLExpression base, SkTArray<Wrapper<DSLExpression>> args) {
-    return {{base(std::move(args))}};
+    return {{DSLExpression(base(std::move(args), this->position(offset)), this->position(offset))}};
 }
 
 /* LBRACKET expression? RBRACKET | DOT IDENTIFIER | LPAREN arguments RPAREN |
@@ -1562,17 +1554,20 @@
     }
     switch (next.fKind) {
         case Token::Kind::TK_LBRACKET: {
+            if (this->checkNext(Token::Kind::TK_RBRACKET)) {
+                this->error(next, "missing index in '[]'");
+                return {{DSLExpression::Poison()}};
+            }
             skstd::optional<DSLWrapper<DSLExpression>> index = this->expression();
             if (!index) {
                 return skstd::nullopt;
             }
             this->expect(Token::Kind::TK_RBRACKET, "']' to complete array access expression");
             DSLPossibleExpression result = base[std::move(**index)];
-            if (result.valid()) {
-                return {{std::move(result)}};
+            if (!result.valid()) {
+                result.reportErrors(this->position(next));
             }
-            result.reportErrors(PositionInfo());
-            return skstd::nullopt;
+            return {{std::move(result)}};
         }
         case Token::Kind::TK_DOT: {
             int offset = this->peek().fOffset;
@@ -1633,21 +1628,21 @@
         case Token::Kind::TK_IDENTIFIER: {
             skstd::string_view text;
             if (this->identifier(&text)) {
-                return dsl::Symbol(text);
+                return dsl::Symbol(text, this->position(t));
             }
             break;
         }
         case Token::Kind::TK_INT_LITERAL: {
             SKSL_INT i;
             if (this->intLiteral(&i)) {
-                return {{i}};
+                return {{DSLExpression(i, this->position(t))}};
             }
             break;
         }
         case Token::Kind::TK_FLOAT_LITERAL: {
             SKSL_FLOAT f;
             if (this->floatLiteral(&f)) {
-                return {{f}};
+                return {{DSLExpression(f, this->position(t))}};
             }
             break;
         }
@@ -1655,7 +1650,7 @@
         case Token::Kind::TK_FALSE_LITERAL: {
             bool b;
             if (this->boolLiteral(&b)) {
-                return {{b}};
+                return {{DSLExpression(b, this->position(t))}};
             }
             break;
         }
diff --git a/src/sksl/SkSLDSLParser.h b/src/sksl/SkSLDSLParser.h
index 11b9f60..d9e9b62 100644
--- a/src/sksl/SkSLDSLParser.h
+++ b/src/sksl/SkSLDSLParser.h
@@ -68,7 +68,9 @@
 
     skstd::string_view text(Token token);
 
-    Position position(Token token);
+    PositionInfo position(Token token);
+
+    PositionInfo position(int offset);
 
 private:
     static void InitLayoutMap();
@@ -121,9 +123,6 @@
      */
     bool expectIdentifier(Token* result);
 
-    ErrorReporter& errors() {
-        return *fErrorReporter;
-    }
     void error(Token token, String msg);
     void error(int offset, String msg);
 
@@ -137,6 +136,8 @@
 
     ASTNode::ID precision();
 
+    SKSL_INT arraySize();
+
     bool declaration();
 
     bool functionDeclarationEnd(const dsl::DSLModifiers& modifiers,
@@ -144,9 +145,10 @@
                                 const Token& name);
 
     struct VarDeclarationsPrefix {
-        dsl::DSLModifiers modifiers;
-        dsl::DSLType type = dsl::DSLType(dsl::kVoid_Type);
-        Token name;
+        PositionInfo fPosition;
+        dsl::DSLModifiers fModifiers;
+        dsl::DSLType fType = dsl::DSLType(dsl::kVoid_Type);
+        Token fName;
     };
 
     bool varDeclarationsPrefix(VarDeclarationsPrefix* prefixData);
@@ -162,8 +164,8 @@
     /* (LBRACKET expression? RBRACKET)* (EQ assignmentExpression)? (COMMA IDENTIFER
        (LBRACKET expression? RBRACKET)* (EQ assignmentExpression)?)* SEMICOLON */
     template<class T>
-    SkTArray<T> varDeclarationEnd(const dsl::DSLModifiers& mods, dsl::DSLType baseType,
-                                  skstd::string_view name);
+    SkTArray<T> varDeclarationEnd(PositionInfo position, const dsl::DSLModifiers& mods,
+                                  dsl::DSLType baseType, skstd::string_view name);
 
     SkTArray<dsl::DSLGlobalVar> globalVarDeclarationEnd(const dsl::DSLModifiers& modifiers,
                                                         dsl::DSLType type, skstd::string_view name);
@@ -335,7 +337,7 @@
     ErrorReporter* fErrorReporter;
     bool fEncounteredFatalError;
     ProgramKind fKind;
-    String fText;
+    std::unique_ptr<String> fText;
     Lexer fLexer;
     // current parse depth, used to enforce a recursion limit to try to keep us from overflowing the
     // stack on pathological inputs
diff --git a/src/sksl/SkSLErrorReporter.cpp b/src/sksl/SkSLErrorReporter.cpp
index 49a66a4..0ef8f66 100644
--- a/src/sksl/SkSLErrorReporter.cpp
+++ b/src/sksl/SkSLErrorReporter.cpp
@@ -7,16 +7,25 @@
 
 #include "include/sksl/SkSLErrorReporter.h"
 
+#include "src/sksl/SkSLCompiler.h"
 #include "src/sksl/dsl/priv/DSLWriter.h"
 
 namespace SkSL {
 
 void ErrorReporter::error(const char* msg, PositionInfo position) {
+    if (strstr(msg, Compiler::POISON_TAG)) {
+        // don't report errors on poison values
+        return;
+    }
     ++fErrorCount;
     this->handleError(msg, position);
 }
 
 void ErrorReporter::error(int offset, const char* msg) {
+    if (strstr(msg, Compiler::POISON_TAG)) {
+        // don't report errors on poison values
+        return;
+    }
     if (offset == -1) {
         ++fErrorCount;
         fPendingErrors.push_back(std::string(msg));
diff --git a/src/sksl/dsl/DSLCore.cpp b/src/sksl/dsl/DSLCore.cpp
index e6a5326..f5a6d5b 100644
--- a/src/sksl/dsl/DSLCore.cpp
+++ b/src/sksl/dsl/DSLCore.cpp
@@ -121,12 +121,12 @@
         return ir.call(/*offset=*/-1, ir.convertIdentifier(-1, name), std::move(argArray));
     }
 
-    static DSLStatement Break() {
-        return SkSL::BreakStatement::Make(/*offset=*/-1);
+    static DSLStatement Break(PositionInfo pos) {
+        return SkSL::BreakStatement::Make(pos.offset());
     }
 
-    static DSLStatement Continue() {
-        return SkSL::ContinueStatement::Make(/*offset=*/-1);
+    static DSLStatement Continue(PositionInfo pos) {
+        return SkSL::ContinueStatement::Make(pos.offset());
     }
 
     static DSLStatement Declare(DSLVar& var, PositionInfo pos) {
@@ -323,12 +323,12 @@
     return DSLCore::sk_Position();
 }
 
-DSLStatement Break() {
-    return DSLCore::Break();
+DSLStatement Break(PositionInfo pos) {
+    return DSLCore::Break(pos);
 }
 
-DSLStatement Continue() {
-    return DSLCore::Continue();
+DSLStatement Continue(PositionInfo pos) {
+    return DSLCore::Continue(pos);
 }
 
 // Logically, we'd want the variable's initial value to appear on here in Declare, since that
diff --git a/src/sksl/dsl/DSLExpression.cpp b/src/sksl/dsl/DSLExpression.cpp
index fde1ab3..134318b 100644
--- a/src/sksl/dsl/DSLExpression.cpp
+++ b/src/sksl/dsl/DSLExpression.cpp
@@ -39,9 +39,9 @@
     DSLWriter::ReportErrors();
 }
 
-DSLExpression::DSLExpression(float value)
+DSLExpression::DSLExpression(float value, PositionInfo pos)
     : fExpression(SkSL::FloatLiteral::Make(DSLWriter::Context(),
-                                           /*offset=*/-1,
+                                           pos.offset(),
                                            value)) {
     if (!isfinite(value)) {
         if (isinf(value)) {
@@ -52,32 +52,32 @@
     }
 }
 
-DSLExpression::DSLExpression(int value)
+DSLExpression::DSLExpression(int value, PositionInfo pos)
     : fExpression(SkSL::IntLiteral::Make(DSLWriter::Context(),
-                                         /*offset=*/-1,
+                                         pos.offset(),
                                          value)) {}
 
-DSLExpression::DSLExpression(int64_t value)
+DSLExpression::DSLExpression(int64_t value, PositionInfo pos)
     : fExpression(SkSL::IntLiteral::Make(DSLWriter::Context(),
-                                         /*offset=*/-1,
+                                         pos.offset(),
                                          value)) {}
 
-DSLExpression::DSLExpression(unsigned int value)
+DSLExpression::DSLExpression(unsigned int value, PositionInfo pos)
     : fExpression(SkSL::IntLiteral::Make(DSLWriter::Context(),
-                                         /*offset=*/-1,
+                                         pos.offset(),
                                          value)) {}
 
-DSLExpression::DSLExpression(bool value)
+DSLExpression::DSLExpression(bool value, PositionInfo pos)
     : fExpression(SkSL::BoolLiteral::Make(DSLWriter::Context(),
-                                          /*offset=*/-1,
+                                          pos.offset(),
                                           value)) {}
 
-DSLExpression::DSLExpression(DSLVarBase& var) {
-    fExpression = std::make_unique<SkSL::VariableReference>(/*offset=*/-1, DSLWriter::Var(var),
+DSLExpression::DSLExpression(DSLVarBase& var, PositionInfo pos) {
+    fExpression = std::make_unique<SkSL::VariableReference>(pos.offset(), DSLWriter::Var(var),
             SkSL::VariableReference::RefKind::kRead);
 }
 
-DSLExpression::DSLExpression(DSLVarBase&& var)
+DSLExpression::DSLExpression(DSLVarBase&& var, PositionInfo pos)
     : DSLExpression(var) {}
 
 DSLExpression::DSLExpression(DSLPossibleExpression expr, PositionInfo pos) {
@@ -85,10 +85,14 @@
     if (expr.valid()) {
         fExpression = std::move(expr.fExpression);
     } else {
-        fExpression = SkSL::Poison::Make(DSLWriter::Context());
+        fExpression = SkSL::Poison::Make(pos.offset(), DSLWriter::Context());
     }
 }
 
+DSLExpression DSLExpression::Poison(PositionInfo pos) {
+    return DSLExpression(SkSL::Poison::Make(pos.offset(), DSLWriter::Context()));
+}
+
 DSLExpression::~DSLExpression() {
 #if !defined(SKSL_STANDALONE) && SK_SUPPORT_GPU
     if (fExpression && DSLWriter::InFragmentProcessor()) {
@@ -166,13 +170,14 @@
     return DSLWriter::ConvertIndex(this->release(), right.release());
 }
 
-DSLPossibleExpression DSLExpression::operator()(SkTArray<DSLWrapper<DSLExpression>> args) {
+DSLPossibleExpression DSLExpression::operator()(SkTArray<DSLWrapper<DSLExpression>> args,
+                                                PositionInfo pos) {
     ExpressionArray converted;
     converted.reserve_back(args.count());
     for (DSLWrapper<DSLExpression>& arg : args) {
         converted.push_back(arg->release());
     }
-    return DSLWriter::Call(this->release(), std::move(converted));
+    return DSLWriter::Call(this->release(), std::move(converted), pos);
 }
 
 #define OP(op, token)                                                                              \
@@ -338,8 +343,9 @@
     return DSLExpression(this->release())[std::move(index)];
 }
 
-DSLPossibleExpression DSLPossibleExpression::operator()(SkTArray<DSLWrapper<DSLExpression>> args) {
-    return DSLExpression(this->release())(std::move(args));
+DSLPossibleExpression DSLPossibleExpression::operator()(SkTArray<DSLWrapper<DSLExpression>> args,
+                                                        PositionInfo pos) {
+    return DSLExpression(this->release())(std::move(args), pos);
 }
 
 DSLPossibleExpression DSLPossibleExpression::operator++() {
diff --git a/src/sksl/dsl/DSLFunction.cpp b/src/sksl/dsl/DSLFunction.cpp
index 78bafb0..1c9256c 100644
--- a/src/sksl/dsl/DSLFunction.cpp
+++ b/src/sksl/dsl/DSLFunction.cpp
@@ -20,7 +20,7 @@
 namespace dsl {
 
 void DSLFunction::init(DSLModifiers modifiers, const DSLType& returnType, skstd::string_view name,
-                       SkTArray<DSLParameter*> params) {
+                       SkTArray<DSLParameter*> params, PositionInfo pos) {
     // Conservatively assume all user-defined functions have side effects.
     if (!DSLWriter::IsModule()) {
         modifiers.fModifiers.fFlags |= Modifiers::kHasSideEffects_Flag;
@@ -51,7 +51,7 @@
     SkASSERT(paramVars.size() == params.size());
     fDecl = SkSL::FunctionDeclaration::Convert(DSLWriter::Context(),
                                                *DSLWriter::SymbolTable(),
-                                               /*offset=*/-1,
+                                               pos.offset(),
                                                DSLWriter::Modifiers(modifiers.fModifiers),
                                                name == "main" ? name : DSLWriter::Name(name),
                                                std::move(paramVars), &returnType.skslType(),
@@ -66,7 +66,7 @@
         // case the definition is delayed. If we end up defining the function immediately, we'll
         // remove the prototype in define().
         DSLWriter::ProgramElements().push_back(std::make_unique<SkSL::FunctionPrototype>(
-                /*offset=*/-1, fDecl, DSLWriter::IsModule()));
+                pos.offset(), fDecl, DSLWriter::IsModule()));
     }
 }
 
diff --git a/src/sksl/dsl/DSLSymbols.cpp b/src/sksl/dsl/DSLSymbols.cpp
index 52356e7..b490de4 100644
--- a/src/sksl/dsl/DSLSymbols.cpp
+++ b/src/sksl/dsl/DSLSymbols.cpp
@@ -26,8 +26,8 @@
     return DSLWriter::IRGenerator().symbolTable();
 }
 
-DSLPossibleExpression Symbol(skstd::string_view name) {
-    return DSLWriter::IRGenerator().convertIdentifier(/*offset=*/-1, name);
+DSLPossibleExpression Symbol(skstd::string_view name, PositionInfo pos) {
+    return DSLWriter::IRGenerator().convertIdentifier(pos.offset(), name);
 }
 
 bool IsType(skstd::string_view name) {
diff --git a/src/sksl/dsl/DSLType.cpp b/src/sksl/dsl/DSLType.cpp
index 75e16d8..f4efe9f 100644
--- a/src/sksl/dsl/DSLType.cpp
+++ b/src/sksl/dsl/DSLType.cpp
@@ -204,25 +204,25 @@
     return DSLWriter::Construct(type.skslType(), std::move(argArray));
 }
 
-DSLType Array(const DSLType& base, int count) {
+DSLType Array(const DSLType& base, int count, PositionInfo pos) {
     if (count <= 0) {
-        DSLWriter::ReportError("array size must be positive");
+        DSLWriter::ReportError("array size must be positive", pos);
         return base;
     }
     if (base.isArray()) {
-        DSLWriter::ReportError("multidimensional arrays are not permitted");
-        return base;
+        DSLWriter::ReportError("multi-dimensional arrays are not supported", pos);
+        return kPoison_Type;
     }
     return DSLWriter::SymbolTable()->addArrayDimension(&base.skslType(), count);
 }
 
-DSLType Struct(skstd::string_view name, SkSpan<DSLField> fields) {
+DSLType Struct(skstd::string_view name, SkSpan<DSLField> fields, PositionInfo pos) {
     std::vector<SkSL::Type::Field> skslFields;
     skslFields.reserve(fields.size());
     for (const DSLField& field : fields) {
         skslFields.emplace_back(field.fModifiers.fModifiers, field.fName, &field.fType.skslType());
     }
-    const SkSL::Type* result = DSLWriter::SymbolTable()->add(Type::MakeStructType(/*offset=*/-1,
+    const SkSL::Type* result = DSLWriter::SymbolTable()->add(Type::MakeStructType(pos.offset(),
                                                                                   name,
                                                                                   skslFields));
     DSLWriter::ProgramElements().push_back(std::make_unique<SkSL::StructDefinition>(/*offset=*/-1,
diff --git a/src/sksl/dsl/DSLVar.cpp b/src/sksl/dsl/DSLVar.cpp
index 188c41a..9e89be4 100644
--- a/src/sksl/dsl/DSLVar.cpp
+++ b/src/sksl/dsl/DSLVar.cpp
@@ -21,23 +21,26 @@
 
 namespace dsl {
 
-DSLVarBase::DSLVarBase(DSLType type, skstd::string_view name, DSLExpression initialValue)
-    : DSLVarBase(DSLModifiers(), std::move(type), name, std::move(initialValue)) {}
+DSLVarBase::DSLVarBase(DSLType type, skstd::string_view name, DSLExpression initialValue,
+                       PositionInfo pos)
+    : DSLVarBase(DSLModifiers(), std::move(type), name, std::move(initialValue), pos) {}
 
-DSLVarBase::DSLVarBase(DSLType type, DSLExpression initialValue)
-    : DSLVarBase(type, "var", std::move(initialValue)) {}
+DSLVarBase::DSLVarBase(DSLType type, DSLExpression initialValue, PositionInfo pos)
+    : DSLVarBase(type, "var", std::move(initialValue), pos) {}
 
-DSLVarBase::DSLVarBase(const DSLModifiers& modifiers, DSLType type, DSLExpression initialValue)
-    : DSLVarBase(modifiers, type, "var", std::move(initialValue)) {}
+DSLVarBase::DSLVarBase(const DSLModifiers& modifiers, DSLType type, DSLExpression initialValue,
+                       PositionInfo pos)
+    : DSLVarBase(modifiers, type, "var", std::move(initialValue), pos) {}
 
 DSLVarBase::DSLVarBase(const DSLModifiers& modifiers, DSLType type, skstd::string_view name,
-                       DSLExpression initialValue)
+                       DSLExpression initialValue, PositionInfo pos)
     : fModifiers(std::move(modifiers))
     , fType(std::move(type))
     , fRawName(name)
     , fName(fType.skslType().isOpaque() ? name : DSLWriter::Name(name))
     , fInitialValue(std::move(initialValue))
-    , fDeclared(DSLWriter::MarkVarsDeclared()) {
+    , fDeclared(DSLWriter::MarkVarsDeclared())
+    , fPosition(pos) {
     if (fModifiers.fModifiers.fFlags & Modifiers::kUniform_Flag) {
 #if SK_SUPPORT_GPU && !defined(SKSL_STANDALONE)
         if (DSLWriter::InFragmentProcessor()) {
@@ -91,6 +94,7 @@
     std::swap(fInitialValue.fExpression, other.fInitialValue.fExpression);
     std::swap(fDeclared, other.fDeclared);
     std::swap(fInitialized, other.fInitialized);
+    std::swap(fPosition, other.fPosition);
 }
 
 void DSLVar::swap(DSLVar& other) {
@@ -102,7 +106,7 @@
 }
 
 DSLGlobalVar::DSLGlobalVar(const char* name)
-    : INHERITED(kVoid_Type, name, DSLExpression()) {
+    : INHERITED(kVoid_Type, name, DSLExpression(), PositionInfo()) {
     fName = name;
     DSLWriter::MarkDeclared(*this);
 #if SK_SUPPORT_GPU && !defined(SKSL_STANDALONE)
diff --git a/src/sksl/dsl/priv/DSLWriter.cpp b/src/sksl/dsl/priv/DSLWriter.cpp
index d37afcb..9ab3380 100644
--- a/src/sksl/dsl/priv/DSLWriter.cpp
+++ b/src/sksl/dsl/priv/DSLWriter.cpp
@@ -136,17 +136,19 @@
 #endif // !defined(SKSL_STANDALONE) && SK_SUPPORT_GPU
 
 std::unique_ptr<SkSL::Expression> DSLWriter::Call(const FunctionDeclaration& function,
-                                                  ExpressionArray arguments) {
+                                                  ExpressionArray arguments,
+                                                  PositionInfo pos) {
     // We can't call FunctionCall::Convert directly here, because intrinsic management is handled in
     // IRGenerator::call.
-    return IRGenerator().call(/*offset=*/-1, function, std::move(arguments));
+    return IRGenerator().call(pos.offset(), function, std::move(arguments));
 }
 
 std::unique_ptr<SkSL::Expression> DSLWriter::Call(std::unique_ptr<SkSL::Expression> expr,
-                                                  ExpressionArray arguments) {
+                                                  ExpressionArray arguments,
+                                                  PositionInfo pos) {
     // We can't call FunctionCall::Convert directly here, because intrinsic management is handled in
     // IRGenerator::call.
-    return IRGenerator().call(/*offset=*/-1, std::move(expr), std::move(arguments));
+    return IRGenerator().call(pos.offset(), std::move(expr), std::move(arguments));
 }
 
 DSLPossibleExpression DSLWriter::Coerce(std::unique_ptr<Expression> expr, const SkSL::Type& type) {
@@ -239,13 +241,13 @@
         SkASSERT(!var.fVar);
         var.fInitialized = true;
         if (var.storage() != SkSL::VariableStorage::kParameter) {
-            DSLWriter::IRGenerator().checkVarDeclaration(/*offset=*/-1,
+            DSLWriter::IRGenerator().checkVarDeclaration(var.fPosition.offset(),
                                                          var.fModifiers.fModifiers,
                                                          &var.fType.skslType(),
                                                          var.storage());
         }
         std::unique_ptr<SkSL::Variable> skslvar = DSLWriter::IRGenerator().convertVar(
-                                                                          /*offset=*/-1,
+                                                                          var.fPosition.offset(),
                                                                           var.fModifiers.fModifiers,
                                                                           &var.fType.skslType(),
                                                                           var.fName,
@@ -278,7 +280,7 @@
     // This should only be called on undeclared parameter variables, but we allow the creation to go
     // ahead regardless so we don't have to worry about null pointers potentially sneaking in and
     // breaking things. DSLFunction is responsible for reporting errors for invalid parameters.
-    return DSLWriter::IRGenerator().convertVar(/*offset=*/-1, var.fModifiers.fModifiers,
+    return DSLWriter::IRGenerator().convertVar(var.fPosition.offset(), var.fModifiers.fModifiers,
                                                &var.fType.skslType(), var.fName, /*isArray=*/false,
                                                /*arraySize=*/nullptr, var.storage());
 }
diff --git a/src/sksl/dsl/priv/DSLWriter.h b/src/sksl/dsl/priv/DSLWriter.h
index 129b6bc..883f0c0 100644
--- a/src/sksl/dsl/priv/DSLWriter.h
+++ b/src/sksl/dsl/priv/DSLWriter.h
@@ -191,13 +191,15 @@
 #endif // !defined(SKSL_STANDALONE) && SK_SUPPORT_GPU
 
     static std::unique_ptr<SkSL::Expression> Call(const FunctionDeclaration& function,
-                                                  ExpressionArray arguments);
+                                                  ExpressionArray arguments,
+                                                  PositionInfo pos = PositionInfo::Capture());
 
     /**
      * Invokes expr(arguments), where expr is a function or type reference.
      */
     static std::unique_ptr<SkSL::Expression> Call(std::unique_ptr<SkSL::Expression> expr,
-                                                  ExpressionArray arguments);
+                                                  ExpressionArray arguments,
+                                                  PositionInfo pos = PositionInfo::Capture());
 
     static DSLPossibleExpression Coerce(std::unique_ptr<Expression> expr, const SkSL::Type& type);
 
diff --git a/src/sksl/ir/SkSLIndexExpression.cpp b/src/sksl/ir/SkSLIndexExpression.cpp
index 7f31226..9e337b5 100644
--- a/src/sksl/ir/SkSLIndexExpression.cpp
+++ b/src/sksl/ir/SkSLIndexExpression.cpp
@@ -43,7 +43,7 @@
         if (!arraySize) {
             return nullptr;
         }
-        return std::make_unique<TypeReference>(context, /*offset=*/-1,
+        return std::make_unique<TypeReference>(context, base->fOffset,
                                                symbolTable.addArrayDimension(&baseType, arraySize));
     }
     // Convert an index expression with an expression inside of it: `arr[a * 3]`.
diff --git a/src/sksl/ir/SkSLPoison.h b/src/sksl/ir/SkSLPoison.h
index 43e3a1c..a6b58fc 100644
--- a/src/sksl/ir/SkSLPoison.h
+++ b/src/sksl/ir/SkSLPoison.h
@@ -14,19 +14,19 @@
 public:
     static constexpr Kind kExpressionKind = Kind::kPoison;
 
-    static std::unique_ptr<Expression> Make(const Context& context) {
-        return std::make_unique<Poison>(context.fTypes.fPoison.get());
+    static std::unique_ptr<Expression> Make(int offset, const Context& context) {
+        return std::make_unique<Poison>(offset, context.fTypes.fPoison.get());
     }
 
-    Poison(const Type* type)
-        : INHERITED(/*offset=*/-1, kExpressionKind, type) {}
+    Poison(int offset, const Type* type)
+        : INHERITED(offset, kExpressionKind, type) {}
 
     bool hasProperty(Property property) const override {
         return false;
     }
 
     std::unique_ptr<Expression> clone() const override {
-        return std::make_unique<Poison>(&this->type());
+        return std::make_unique<Poison>(fOffset, &this->type());
     }
 
     String description() const override {
diff --git a/tests/SkSLDSLTest.cpp b/tests/SkSLDSLTest.cpp
index 2349249..6bfad93 100644
--- a/tests/SkSLDSLTest.cpp
+++ b/tests/SkSLDSLTest.cpp
@@ -566,7 +566,7 @@
     }
 
     {
-        ExpectError error(r, "multidimensional arrays are not permitted");
+        ExpectError error(r, "multi-dimensional arrays are not supported");
         Array(Array(kFloat_Type, 2), 2);
     }
 }