Added DSLWrapper so DSL classes can be used in containers

This will be used by the upcoming DSLParser, which needs to be able to
put DSLExpression and DSLVar into containers such as std::vector and
std::optional.

Change-Id: I8d367cfd0b3a852a368c69a5b3be6c0eaa41d74a
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/404156
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
diff --git a/include/sksl/DSL.h b/include/sksl/DSL.h
index b0585b3..4c6cbb2 100644
--- a/include/sksl/DSL.h
+++ b/include/sksl/DSL.h
@@ -22,6 +22,7 @@
 using Modifiers = DSLModifiers;
 using Statement = DSLStatement;
 using Var = DSLVar;
+template<typename T> using Wrapper = DSLWrapper<T>;
 
 } // namespace dsl
 
diff --git a/include/sksl/DSLCore.h b/include/sksl/DSLCore.h
index a524cb3..5b43ed1 100644
--- a/include/sksl/DSLCore.h
+++ b/include/sksl/DSLCore.h
@@ -18,6 +18,7 @@
 #include "include/sksl/DSLStatement.h"
 #include "include/sksl/DSLType.h"
 #include "include/sksl/DSLVar.h"
+#include "include/sksl/DSLWrapper.h"
 
 namespace SkSL {
 
diff --git a/include/sksl/DSLExpression.h b/include/sksl/DSLExpression.h
index 0189acb..3d6b523 100644
--- a/include/sksl/DSLExpression.h
+++ b/include/sksl/DSLExpression.h
@@ -112,6 +112,8 @@
 private:
     DSLExpression(std::unique_ptr<SkSL::Expression> expression);
 
+    void swap(DSLExpression& other);
+
     /**
      * Invalidates this object and returns the SkSL expression it represents coerced to the
      * specified type. If the expression cannot be coerced, reports an error and returns null.
@@ -127,6 +129,7 @@
     friend class DSLPossibleExpression;
     friend class DSLVar;
     friend class DSLWriter;
+    template<typename T> friend class DSLWrapper;
 };
 
 DSLPossibleExpression operator+(DSLExpression left, DSLExpression right);
diff --git a/include/sksl/DSLVar.h b/include/sksl/DSLVar.h
index 218a1ef..b73577e 100644
--- a/include/sksl/DSLVar.h
+++ b/include/sksl/DSLVar.h
@@ -41,7 +41,7 @@
 
     DSLVar(DSLModifiers modifiers, DSLType type, DSLExpression initialValue);
 
-    DSLVar(DSLVar&&) = delete;
+    DSLVar(DSLVar&&) = default;
 
     ~DSLVar();
 
diff --git a/include/sksl/DSLWrapper.h b/include/sksl/DSLWrapper.h
new file mode 100644
index 0000000..fbd4790
--- /dev/null
+++ b/include/sksl/DSLWrapper.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2021 Google LLC.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_DSL_WRAPPER
+#define SKSL_DSL_WRAPPER
+
+#include <memory>
+
+namespace SkSL {
+
+namespace dsl {
+
+/**
+ * Several of the DSL classes override operator= in a non-standard fashion to allow for expressions
+ * like "x = 0" to compile into SkSL code. This makes it impossible to directly use these classes in
+ * C++ containers which expect standard behavior for operator=.
+ *
+ * Wrapper<T> contains a T, where T is a DSL class with non-standard operator=, and provides
+ * standard behavior for operator=, permitting it to be used in standard containers.
+ */
+template<typename T>
+class DSLWrapper {
+public:
+    DSLWrapper(T value) {
+        fValue.swap(value);
+    }
+
+    DSLWrapper(const DSLWrapper&) = delete;
+
+    DSLWrapper(DSLWrapper&& other) {
+        fValue.swap(other.fValue);
+    }
+
+    T& operator*() {
+        return fValue;
+    }
+
+    T* operator->() {
+        return &fValue;
+    }
+
+    DSLWrapper& operator=(const DSLWrapper&) = delete;
+
+    DSLWrapper& operator=(DSLWrapper&& other) {
+        fValue.swap(other.fValue);
+        return *this;
+    }
+
+private:
+    T fValue;
+};
+
+} // namespace dsl
+
+} // namespace SkSL
+
+#endif
diff --git a/src/sksl/dsl/DSLExpression.cpp b/src/sksl/dsl/DSLExpression.cpp
index 799d3ab..55938c6 100644
--- a/src/sksl/dsl/DSLExpression.cpp
+++ b/src/sksl/dsl/DSLExpression.cpp
@@ -100,6 +100,10 @@
               "Expression destroyed without being incorporated into program");
 }
 
+void DSLExpression::swap(DSLExpression& other) {
+    std::swap(fExpression, other.fExpression);
+}
+
 std::unique_ptr<SkSL::Expression> DSLExpression::release() {
     return std::move(fExpression);
 }
diff --git a/tests/SkSLDSLTest.cpp b/tests/SkSLDSLTest.cpp
index 53c1d0b..dc806f6 100644
--- a/tests/SkSLDSLTest.cpp
+++ b/tests/SkSLDSLTest.cpp
@@ -1790,3 +1790,16 @@
     EXPECT_EQUAL(*DSLWriter::ProgramElements()[2],
                  "struct NestedStruct { int x; SimpleStruct simple; };");
 }
+
+DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLWrapper, r, ctxInfo) {
+    AutoDSLContext context(ctxInfo.directContext()->priv().getGpu());
+    std::vector<Wrapper<DSLExpression>> exprs;
+    exprs.push_back(DSLExpression(1));
+    exprs.emplace_back(2.0);
+    EXPECT_EQUAL(std::move(*exprs[0]), "1");
+    EXPECT_EQUAL(std::move(*exprs[1]), "2.0");
+
+    std::vector<Wrapper<DSLVar>> vars;
+    vars.emplace_back(DSLVar(kInt_Type, "x"));
+    REPORTER_ASSERT(r, DSLWriter::Var(*vars[0]).name() == "x");
+}