diff --git a/src/Func.cpp b/src/Func.cpp
index e9c9f75edee9..65113ac2ddc5 100644
--- a/src/Func.cpp
+++ b/src/Func.cpp
@@ -323,6 +323,19 @@ bool var_name_match(const string &candidate, const string &var) {
     }
     return Internal::ends_with(candidate, "." + var);
 }
+
+bool dim_match(const Dim &candidate, const VarOrRVar &var) {
+    if (var_name_match(candidate.var, var.name())) {
+        user_assert(candidate.is_rvar() == var.is_rvar)
+            << (var.is_rvar ? "RVar " : "Var ") << var.name()
+            << " used in scheduling directive has the same name as existing "
+            << (candidate.is_rvar() ? "RVar " : "Var ") << candidate.var << "\n";
+        return true;
+    } else {
+        return false;
+    }
+}
+
 }  // namespace
 
 std::string Stage::name() const {
@@ -455,7 +468,7 @@ void Stage::set_dim_type(const VarOrRVar &var, ForType t) {
     bool found = false;
     vector<Dim> &dims = definition.schedule().dims();
     for (auto &dim : dims) {
-        if (var_name_match(dim.var, var.name())) {
+        if (dim_match(dim, var)) {
             found = true;
             dim.for_type = t;
 
@@ -523,7 +536,7 @@ void Stage::set_dim_device_api(const VarOrRVar &var, DeviceAPI device_api) {
     bool found = false;
     vector<Dim> &dims = definition.schedule().dims();
     for (auto &dim : dims) {
-        if (var_name_match(dim.var, var.name())) {
+        if (dim_match(dim, var)) {
             found = true;
             dim.device_api = device_api;
         }
@@ -1129,7 +1142,7 @@ void Stage::split(const string &old, const string &outer, const string &inner, c
     string inner_name, outer_name, old_name;
 
     for (size_t i = 0; (!found) && i < dims.size(); i++) {
-        if (var_name_match(dims[i].var, old)) {
+        if (dim_match(dims[i], VarOrRVar(old, exact))) {
             found = true;
             old_name = dims[i].var;
             inner_name = old_name + "." + inner;
@@ -1321,7 +1334,7 @@ Stage &Stage::fuse(const VarOrRVar &inner, const VarOrRVar &outer, const VarOrRV
 
     DimType outer_type = DimType::PureRVar;
     for (size_t i = 0; (!found_outer) && i < dims.size(); i++) {
-        if (var_name_match(dims[i].var, outer.name())) {
+        if (dim_match(dims[i], outer)) {
             found_outer = true;
             outer_name = dims[i].var;
             outer_type = dims[i].dim_type;
@@ -1337,7 +1350,7 @@ Stage &Stage::fuse(const VarOrRVar &inner, const VarOrRVar &outer, const VarOrRV
     }
 
     for (size_t i = 0; (!found_inner) && i < dims.size(); i++) {
-        if (var_name_match(dims[i].var, inner.name())) {
+        if (dim_match(dims[i], inner)) {
             found_inner = true;
             inner_name = dims[i].var;
             fused_name = inner_name + "." + fused.name();
@@ -1450,7 +1463,7 @@ Stage &Stage::purify(const VarOrRVar &old_var, const VarOrRVar &new_var) {
     vector<Dim> &dims = schedule.dims();
 
     for (size_t i = 0; (!found) && i < dims.size(); i++) {
-        if (var_name_match(dims[i].var, old_var.name())) {
+        if (dim_match(dims[i], old_var)) {
             found = true;
             old_name = dims[i].var;
             dims[i].var = new_name;
@@ -1592,7 +1605,7 @@ Stage &Stage::rename(const VarOrRVar &old_var, const VarOrRVar &new_var) {
     string old_name;
     vector<Dim> &dims = schedule.dims();
     for (size_t i = 0; (!found) && i < dims.size(); i++) {
-        if (var_name_match(dims[i].var, old_var.name())) {
+        if (dim_match(dims[i], old_var)) {
             found = true;
             old_name = dims[i].var;
             dims[i].var += "." + new_var.name();
@@ -1735,7 +1748,7 @@ Stage &Stage::partition(const VarOrRVar &var, Partition policy) {
     bool found = false;
     vector<Dim> &dims = definition.schedule().dims();
     for (auto &dim : dims) {
-        if (var_name_match(dim.var, var.name())) {
+        if (dim_match(dim, var)) {
             found = true;
             dim.partition_policy = policy;
         }
@@ -1851,7 +1864,7 @@ Stage &Stage::reorder(const std::vector<VarOrRVar> &vars) {
     for (size_t i = 0; i < vars.size(); i++) {
         bool found = false;
         for (size_t j = 0; j < dims.size(); j++) {
-            if (var_name_match(dims[j].var, vars[i].name())) {
+            if (dim_match(dims[j], vars[i])) {
                 idx[i] = j;
                 found = true;
             }
diff --git a/src/autoschedulers/adams2019/LoopNest.cpp b/src/autoschedulers/adams2019/LoopNest.cpp
index 2e0cf3bcf8d0..05c94feadd83 100644
--- a/src/autoschedulers/adams2019/LoopNest.cpp
+++ b/src/autoschedulers/adams2019/LoopNest.cpp
@@ -1692,7 +1692,7 @@ void LoopNest::apply(LoopLevel here,
             for (size_t i = 0; i < symbolic_loop.size(); i++) {
                 StageScheduleState::FuncVar fv;
                 const auto &l = symbolic_loop[i];
-                fv.var = VarOrRVar(l.var, !l.pure);
+                fv.var = VarOrRVar(l.var, l.rvar);
                 fv.orig = fv.var;
                 fv.accessor = l.accessor;
                 const auto &p = parent_bounds->loops(stage->index, i);
diff --git a/src/autoschedulers/adams2019/cost_model_schedule.h b/src/autoschedulers/adams2019/cost_model_schedule.h
index d871e4dbf5b7..beffad156717 100644
--- a/src/autoschedulers/adams2019/cost_model_schedule.h
+++ b/src/autoschedulers/adams2019/cost_model_schedule.h
@@ -63,8 +63,8 @@ inline void do_cost_model_schedule(Halide::Pipeline pipeline) {
     Var n(sum.get_schedule().dims()[0].var);
     Var ni("ni");
     Var nii("nii");
-    Var r1010_z(filter1_im_0_d_def__.update(0).get_schedule().dims()[2].var);
-    Var r1207_y(filter1_im_0_d_def__.update(1).get_schedule().dims()[1].var);
+    RVar r1010_z(filter1_im_0_d_def__.update(0).get_schedule().dims()[2].var);
+    RVar r1207_y(filter1_im_0_d_def__.update(1).get_schedule().dims()[1].var);
     Var s(squashed_head1_filter_0_d_def__.get_schedule().dims()[1].var);
     Var si("si");
     Var v12(head2_bias_im_0_d_def__.get_schedule().dims()[0].var);
diff --git a/src/autoschedulers/anderson2021/LoopNest.cpp b/src/autoschedulers/anderson2021/LoopNest.cpp
index ab7510d633a7..53b76a513d2d 100644
--- a/src/autoschedulers/anderson2021/LoopNest.cpp
+++ b/src/autoschedulers/anderson2021/LoopNest.cpp
@@ -3931,7 +3931,7 @@ void LoopNest::apply(LoopLevel here,
             for (size_t i = 0; i < symbolic_loop.size(); i++) {
                 StageScheduleState::FuncVar fv;
                 const auto &l = symbolic_loop[i];
-                fv.var = VarOrRVar(l.var, !l.pure);
+                fv.var = VarOrRVar(l.var, l.rvar);
                 fv.orig = fv.var;
                 fv.accessor = l.accessor;
                 const auto &p = parent_bounds->loops(stage->index, i);
diff --git a/src/autoschedulers/anderson2021/cost_model_schedule.h b/src/autoschedulers/anderson2021/cost_model_schedule.h
index 8e560e49dcd7..a8f42e919385 100644
--- a/src/autoschedulers/anderson2021/cost_model_schedule.h
+++ b/src/autoschedulers/anderson2021/cost_model_schedule.h
@@ -66,8 +66,8 @@ inline void do_cost_model_schedule(Halide::Pipeline pipeline) {
     Var ni("ni");
     Var nii("nii");
     Var niii("niii");
-    Var r1316_z(filter1_im_0_d_def__.update(0).get_schedule().dims()[2].var);
-    Var r1512_y(filter1_im_0_d_def__.update(1).get_schedule().dims()[1].var);
+    RVar r1316_z(filter1_im_0_d_def__.update(0).get_schedule().dims()[2].var);
+    RVar r1512_y(filter1_im_0_d_def__.update(1).get_schedule().dims()[1].var);
     Var s(squashed_head1_filter_0_d_def__.get_schedule().dims()[1].var);
     Var si("si");
     Var v11(bias1_im_0_d_def__.get_schedule().dims()[0].var);
diff --git a/test/correctness/fit_function.cpp b/test/correctness/fit_function.cpp
index 2c3516089cf2..7b6abc4fa5cb 100644
--- a/test/correctness/fit_function.cpp
+++ b/test/correctness/fit_function.cpp
@@ -73,7 +73,7 @@ int main(int argc, char **argv) {
                 // Find a pure var to vectorize over
                 for (auto d : df.update(i).get_schedule().dims()) {
                     if (d.is_pure()) {
-                        df.update(i).vectorize(Var(d.var), 4);
+                        df.update(i).vectorize(VarOrRVar(d.var, d.is_rvar()), 4);
                         break;
                     }
                 }
diff --git a/test/correctness/multi_pass_reduction.cpp b/test/correctness/multi_pass_reduction.cpp
index 22bcb9fbae04..8988973d3909 100644
--- a/test/correctness/multi_pass_reduction.cpp
+++ b/test/correctness/multi_pass_reduction.cpp
@@ -122,10 +122,8 @@ int main(int argc, char **argv) {
         // Walk down the image in vectors
         f.update(0).vectorize(x, 4);
 
-        // Walk across the image in parallel. We need to do an unsafe
-        // reorder operation here to move y to the outer loop, because
-        // we don't have the ability to reorder vars with rvars yet.
-        f.update(1).reorder(Var(r.x.name()), y).parallel(y);
+        // Walk across the image in parallel.
+        f.update(1).reorder(r.x, y).parallel(y);
 
         Buffer<float> result = f.realize({100, 100});
 
diff --git a/test/error/CMakeLists.txt b/test/error/CMakeLists.txt
index 52a2a01cd65e..0478c3b11087 100644
--- a/test/error/CMakeLists.txt
+++ b/test/error/CMakeLists.txt
@@ -105,6 +105,7 @@ tests(GROUPS error
       store_at_without_compute_at.cpp
       thread_id_outside_block_id.cpp
       too_many_args.cpp
+      treat_rvar_as_var.cpp
       tuple_arg_select_undef.cpp
       tuple_output_bounds_check.cpp
       tuple_realization_to_buffer.cpp
diff --git a/test/error/treat_rvar_as_var.cpp b/test/error/treat_rvar_as_var.cpp
new file mode 100644
index 000000000000..5080a8206d42
--- /dev/null
+++ b/test/error/treat_rvar_as_var.cpp
@@ -0,0 +1,21 @@
+#include "Halide.h"
+
+#include <stdio.h>
+
+using namespace Halide;
+
+int main(int argc, char **argv) {
+    Func f;
+    Var x, y;
+
+    RDom r(0, 10);
+    f(x, y) += r;
+
+    // Sneakily disguising an RVar as a Var by reusing the name should result in
+    // an error. Otherwise it can permit schedules that aren't legal.
+    Var xo, xi;
+    f.update().split(Var(r.x.name()), xo, xi, 8, TailStrategy::RoundUp);
+
+    printf("Success!\n");
+    return 0;
+}
