test_that("slice-assign throws error with non-vector inputs", {
  x <- environment()
  expect_error(vec_slice(x, 1L) <- 1L, class = "vctrs_error_scalar_type")
})

test_that("slice-assign throws error with non-vector `value`", {
  x <- 1L
  expect_error(vec_slice(x, 1L) <- NULL, class = "vctrs_error_scalar_type")
  expect_error(
    vec_slice(x, 1L) <- environment(),
    class = "vctrs_error_scalar_type"
  )
})

test_that("assign throws error with non-vector `value`", {
  x <- 1L

  expect_snapshot(error = TRUE, {
    vec_assign(x, 1L, NULL)
  })
  expect_snapshot(error = TRUE, {
    vec_assign(x, 1L, NULL, slice_value = TRUE)
  })

  expect_snapshot(error = TRUE, {
    vec_assign(x, 1L, NULL, value_arg = "foo")
  })
  expect_snapshot(error = TRUE, {
    vec_assign(x, 1L, NULL, slice_value = TRUE, value_arg = "foo")
  })

  expect_snapshot(error = TRUE, {
    vec_assign(x, 1L, environment(), value_arg = "foo")
  })
  expect_snapshot(error = TRUE, {
    vec_assign(x, 1L, environment(), slice_value = TRUE, value_arg = "foo")
  })
})

test_that("can slice-assign NULL", {
  x <- NULL
  vec_slice(x, 1L) <- 1
  expect_identical(x, NULL)
})

test_that("can assign on NULL `x`", {
  x <- NULL
  expect_identical(vec_assign(x, TRUE, 1), NULL)
  expect_identical(vec_assign(x, TRUE, 1, slice_value = TRUE), NULL)
})

test_that("can slice-assign base vectors", {
  x <- rep(FALSE, 3)
  vec_slice(x, 2) <- TRUE
  expect_identical(x, lgl(FALSE, TRUE, FALSE))

  x <- rep(0L, 3)
  vec_slice(x, 2) <- 1L
  expect_identical(x, int(0L, 1L, 0L))

  x <- rep(0., 3)
  vec_slice(x, 2) <- 1
  expect_identical(x, dbl(0, 1, 0))

  x <- rep(0i, 3)
  vec_slice(x, 2) <- 1i
  expect_identical(x, cpl(0i, 1i, 0i))

  x <- rep("", 3)
  vec_slice(x, 2) <- "foo"
  expect_identical(x, chr("", "foo", ""))

  x <- as.raw(rep(0, 3))
  vec_slice(x, 2) <- as.raw(1)
  expect_identical(x, as.raw(c(0, 1, 0)))

  x <- rep(list(NULL), 3)
  vec_slice(x, 2) <- list(NA)
  expect_identical(x, list(NULL, NA, NULL))
})

test_that("can assign base vectors", {
  x <- rep(FALSE, 3)
  expect_identical(vec_assign(x, 2, TRUE), lgl(FALSE, TRUE, FALSE))
  expect_identical(x, rep(FALSE, 3))

  x <- rep(0L, 3)
  expect_identical(vec_assign(x, 2, 1L), int(0L, 1L, 0L))
  expect_identical(x, rep(0L, 3))

  x <- rep(0., 3)
  expect_identical(vec_assign(x, 2, 1), dbl(0, 1, 0))
  expect_identical(x, rep(0., 3))

  x <- rep(0i, 3)
  expect_identical(vec_assign(x, 2, 1i), cpl(0i, 1i, 0i))
  expect_identical(x, rep(0i, 3))

  x <- rep("", 3)
  expect_identical(vec_assign(x, 2, "foo"), chr("", "foo", ""))
  expect_identical(x, rep("", 3))

  x <- as.raw(rep(0, 3))
  expect_identical(vec_assign(x, 2, as.raw(1)), as.raw(c(0, 1, 0)))
  expect_identical(x, as.raw(rep(0, 3)))

  x <- rep(list(NULL), 3)
  expect_identical(vec_assign(x, 2, list(NA)), list(NULL, NA, NULL))
  expect_identical(x, rep(list(NULL), 3))
})

test_that("can assign base vectors with logical indices", {
  # Logical indices have their own optimized path so we test them specially

  condition <- c(TRUE, FALSE, NA, TRUE)

  x <- rep(FALSE, 4)
  value <- c(NA, TRUE, TRUE, TRUE)
  y <- vec_assign(x, condition, vec_slice(value, condition))
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, lgl(NA, FALSE, FALSE, TRUE))

  x <- rep(0L, 4)
  value <- c(NA, 1L, 2L, 3L)
  y <- vec_assign(x, condition, vec_slice(value, condition))
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, int(NA, 0L, 0L, 3L))

  x <- rep(0, 4)
  value <- c(NA, 1, 2, 3)
  y <- vec_assign(x, condition, vec_slice(value, condition))
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, dbl(NA, 0, 0, 3))

  x <- rep(0i, 4)
  na <- complex(real = NA, imaginary = NA)
  value <- c(na, 1i, 2i, 3i)
  y <- vec_assign(x, condition, vec_slice(value, condition))
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, c(na, 0i, 0i, 3i))

  x <- rep("", 4)
  value <- c(NA, "foo", "bar", "baz")
  y <- vec_assign(x, condition, vec_slice(value, condition))
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, c(NA, "", "", "baz"))

  x <- as.raw(rep(0, 4))
  value <- as.raw(c(1, 2, 3, 4))
  y <- vec_assign(x, condition, vec_slice(value, condition))
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, as.raw(c(1, 0, 0, 4)))

  x <- rep(list(1), 4)
  value <- list(NA, 2, 3, 4)
  y <- vec_assign(x, condition, vec_slice(value, condition))
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, list(NA, 1, 1, 4))
})

test_that("can assign base vectors with recycling of `value`", {
  x <- rep(FALSE, 3)
  expect_identical(vec_assign(x, c(3, 1), TRUE), lgl(TRUE, FALSE, TRUE))
  expect_identical(x, rep(FALSE, 3))

  x <- rep(0L, 3)
  expect_identical(vec_assign(x, c(3, 1), 1L), int(1L, 0L, 1L))
  expect_identical(x, rep(0L, 3))

  x <- rep(0., 3)
  expect_identical(vec_assign(x, c(3, 1), 1), dbl(1, 0, 1))
  expect_identical(x, rep(0., 3))

  x <- rep(0i, 3)
  expect_identical(vec_assign(x, c(3, 1), 1i), cpl(1i, 0i, 1i))
  expect_identical(x, rep(0i, 3))

  x <- rep("", 3)
  expect_identical(vec_assign(x, c(3, 1), "foo"), chr("foo", "", "foo"))
  expect_identical(x, rep("", 3))

  x <- as.raw(rep(0, 3))
  expect_identical(vec_assign(x, c(3, 1), as.raw(1)), as.raw(c(1, 0, 1)))
  expect_identical(x, as.raw(rep(0, 3)))

  x <- rep(list(NULL), 3)
  expect_identical(vec_assign(x, c(3, 1), list(NA)), list(NA, NULL, NA))
  expect_identical(x, rep(list(NULL), 3))
})

test_that("can assign base vectors with logical indices with recycled `value`", {
  # Logical indices have their own optimized path so we test them specially

  condition <- c(TRUE, FALSE, NA, TRUE)

  x <- rep(FALSE, 4)
  value <- NA
  y <- vec_assign(x, condition, value)
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, lgl(NA, FALSE, FALSE, NA))

  x <- rep(0L, 4)
  value <- NA
  y <- vec_assign(x, condition, value)
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, int(NA, 0L, 0L, NA))

  x <- rep(0, 4)
  value <- NA
  y <- vec_assign(x, condition, value)
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, dbl(NA, 0, 0, NA))

  x <- rep(0i, 4)
  na <- complex(real = NA, imaginary = NA)
  value <- na
  y <- vec_assign(x, condition, value)
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, c(na, 0i, 0i, na))

  x <- rep("", 4)
  value <- NA
  y <- vec_assign(x, condition, value)
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, c(NA, "", "", NA))

  x <- as.raw(rep(0, 4))
  value <- as.raw(1)
  y <- vec_assign(x, condition, value)
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, as.raw(c(1, 0, 0, 1)))

  x <- rep(list(1), 4)
  value <- NA
  y <- vec_assign(x, condition, value)
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, list(NULL, 1, 1, NULL))
})

test_that("can assign shaped base vectors", {
  mat <- as.matrix

  x <- mat(rep(FALSE, 3))
  expect_identical(vec_assign(x, 2, TRUE), mat(lgl(FALSE, TRUE, FALSE)))
  expect_identical(x, mat(rep(FALSE, 3)))

  x <- mat(rep(0L, 3))
  expect_identical(vec_assign(x, 2, 1L), mat(int(0L, 1L, 0L)))
  expect_identical(x, mat(rep(0L, 3)))

  x <- mat(rep(0, 3))
  expect_identical(vec_assign(x, 2, 1), mat(dbl(0, 1, 0)))
  expect_identical(x, mat(rep(0, 3)))

  x <- mat(rep(0i, 3))
  expect_identical(vec_assign(x, 2, 1i), mat(cpl(0i, 1i, 0i)))
  expect_identical(x, mat(rep(0i, 3)))

  x <- mat(rep("", 3))
  expect_identical(vec_assign(x, 2, "foo"), mat(chr("", "foo", "")))
  expect_identical(x, mat(rep("", 3)))

  x <- mat(as.raw(rep(0, 3)))
  expect_identical(vec_assign(x, 2, as.raw(1)), mat(as.raw(c(0, 1, 0))))
  expect_identical(x, mat(as.raw(rep(0, 3))))

  mat <- as.matrix
  x <- mat(rep(list(NULL), 3))
  expect_identical(vec_assign(x, 2, list(NA)), mat(list(NULL, NA, NULL)))
  expect_identical(x, mat(rep(list(NULL), 3)))
})

test_that("can assign shaped base vectors with logical indices", {
  # Logical indices have their own optimized path so we test them specially

  mat <- as.matrix

  condition <- c(TRUE, FALSE, NA, TRUE)

  x <- mat(rep(FALSE, 4))
  value <- mat(c(NA, TRUE, TRUE, TRUE))
  y <- vec_assign(x, condition, vec_slice(value, condition))
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, mat(c(NA, FALSE, FALSE, TRUE)))

  x <- mat(rep(0L, 4))
  value <- mat(c(NA, 1L, 2L, 3L))
  y <- vec_assign(x, condition, vec_slice(value, condition))
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, mat(c(NA, 0L, 0L, 3L)))

  x <- mat(rep(0, 4))
  value <- mat(c(NA, 1, 2, 3))
  y <- vec_assign(x, condition, vec_slice(value, condition))
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, mat(c(NA, 0, 0, 3)))

  x <- mat(rep(0i, 4))
  value <- mat(c(1i, 2i, 3i, 4i))
  y <- vec_assign(x, condition, vec_slice(value, condition))
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, mat(c(1i, 0i, 0i, 4i)))

  x <- mat(rep("", 4))
  value <- mat(c(NA, "foo", "bar", "baz"))
  y <- vec_assign(x, condition, vec_slice(value, condition))
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, mat(c(NA, "", "", "baz")))

  x <- mat(as.raw(rep(0, 4)))
  value <- mat(as.raw(c(1, 2, 3, 4)))
  y <- vec_assign(x, condition, vec_slice(value, condition))
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, mat(as.raw(c(1, 0, 0, 4))))

  x <- mat(rep(list(1), 4))
  value <- mat(list(NA, 2, 3, 4))
  y <- vec_assign(x, condition, vec_slice(value, condition))
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, mat(list(NA, 1, 1, 4)))
})

test_that("can assign shaped base vectors with recycling of `value`", {
  mat <- as.matrix

  x <- mat(rep(FALSE, 3))
  expect_identical(vec_assign(x, c(3, 1), TRUE), mat(lgl(TRUE, FALSE, TRUE)))
  expect_identical(x, mat(rep(FALSE, 3)))

  x <- mat(rep(0L, 3))
  expect_identical(vec_assign(x, c(3, 1), 1L), mat(int(1L, 0L, 1L)))
  expect_identical(x, mat(rep(0L, 3)))

  x <- mat(rep(0, 3))
  expect_identical(vec_assign(x, c(3, 1), 1), mat(dbl(1, 0, 1)))
  expect_identical(x, mat(rep(0, 3)))

  x <- mat(rep(0i, 3))
  expect_identical(vec_assign(x, c(3, 1), 1i), mat(cpl(1i, 0i, 1i)))
  expect_identical(x, mat(rep(0i, 3)))

  x <- mat(rep("", 3))
  expect_identical(vec_assign(x, c(3, 1), "foo"), mat(chr("foo", "", "foo")))
  expect_identical(x, mat(rep("", 3)))

  x <- mat(as.raw(rep(0, 3)))
  expect_identical(vec_assign(x, c(3, 1), as.raw(1)), mat(as.raw(c(1, 0, 1))))
  expect_identical(x, mat(as.raw(rep(0, 3))))

  mat <- as.matrix
  x <- mat(rep(list(NULL), 3))
  expect_identical(vec_assign(x, c(3, 1), list(NA)), mat(list(NA, NULL, NA)))
  expect_identical(x, mat(rep(list(NULL), 3)))
})

test_that("can assign shaped base vectors with logical indices with recycling of `value`", {
  # Logical indices have their own optimized path so we test them specially

  mat <- as.matrix

  condition <- c(TRUE, FALSE, NA, TRUE)

  x <- mat(rep(FALSE, 4))
  value <- NA
  y <- vec_assign(x, condition, value)
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, mat(c(NA, FALSE, FALSE, NA)))

  x <- mat(rep(0L, 4))
  value <- NA
  y <- vec_assign(x, condition, value)
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, mat(c(NA, 0L, 0L, NA)))

  x <- mat(rep(0, 4))
  value <- NA
  y <- vec_assign(x, condition, value)
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, mat(c(NA, 0, 0, NA)))

  x <- mat(rep(0i, 4))
  value <- NA
  na <- complex(real = NA, imaginary = NA)
  y <- vec_assign(x, condition, value)
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, mat(c(na, 0i, 0i, na)))

  x <- mat(rep("", 4))
  value <- NA
  y <- vec_assign(x, condition, value)
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, mat(c(NA, "", "", NA)))

  x <- mat(as.raw(rep(1, 4)))
  value <- NA
  y <- vec_assign(x, condition, value)
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, mat(as.raw(c(0, 1, 1, 0))))

  x <- mat(rep(list(1), 4))
  value <- NA
  y <- vec_assign(x, condition, value)
  z <- vec_assign(x, condition, value, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, mat(list(NULL, 1, 1, NULL)))
})

test_that("can assign object of any dimensionality", {
  x1 <- ones(3)
  x2 <- ones(3, 4)
  x3 <- ones(3, 4, 5)
  x4 <- ones(3, 4, 5, 6)

  expect_identical(vec_assign(x1, 1L, 2L), array(rep(c(2, 1, 1), 1), dim = 3))
  expect_identical(
    vec_assign(x2, 1L, 2L),
    array(rep(c(2, 1, 1), 3), dim = c(3, 4))
  )
  expect_identical(
    vec_assign(x3, 1L, 2L),
    array(rep(c(2, 1, 1), 12), dim = c(3, 4, 5))
  )
  expect_identical(
    vec_assign(x4, 1L, 2L),
    array(rep(c(2, 1, 1), 60), dim = c(3, 4, 5, 6))
  )

  # With recycling of `value`
  expect_identical(
    vec_assign(x1, c(3L, 2L), 2L),
    array(rep(c(1, 2, 2), 1), dim = 3)
  )
  expect_identical(
    vec_assign(x2, c(3L, 2L), 2L),
    array(rep(c(1, 2, 2), 3), dim = c(3, 4))
  )
  expect_identical(
    vec_assign(x3, c(3L, 2L), 2L),
    array(rep(c(1, 2, 2), 12), dim = c(3, 4, 5))
  )
  expect_identical(
    vec_assign(x4, c(3L, 2L), 2L),
    array(rep(c(1, 2, 2), 60), dim = c(3, 4, 5, 6))
  )
})

test_that("can assign object of any dimensionality with logical indices", {
  # Logical indices have their own optimized path so we test them specially

  # Non barrier (integer, double, logical, etc)
  x1 <- ones(4)
  x2 <- ones(4, 5)
  x3 <- ones(4, 5, 6)
  x4 <- ones(4, 5, 6, 7)

  # Barrier (list, character)
  x1_list <- ones_list(4)
  x2_list <- ones_list(4, 5)
  x3_list <- ones_list(4, 5, 6)
  x4_list <- ones_list(4, 5, 6, 7)

  condition <- c(TRUE, FALSE, NA, TRUE)

  # No recycling
  value <- c(2, 3, 4, 5)
  value_list <- as.list(value)

  y <- vec_assign(x1, condition, vec_slice(value, condition))
  z <- vec_assign(x1, condition, value, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, array(rep(c(2, 1, 1, 5), 1), dim = dim(x1)))

  y <- vec_assign(x1_list, condition, vec_slice(value_list, condition))
  z <- vec_assign(x1_list, condition, value_list, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, array(rep(list(2, 1, 1, 5), 1), dim = dim(x1_list)))

  y <- vec_assign(x2, condition, vec_slice(value, condition))
  z <- vec_assign(x2, condition, value, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, array(rep(c(2, 1, 1, 5), 4), dim = dim(x2)))

  y <- vec_assign(x2_list, condition, vec_slice(value_list, condition))
  z <- vec_assign(x2_list, condition, value_list, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, array(rep(list(2, 1, 1, 5), 4), dim = dim(x2_list)))

  y <- vec_assign(x3, condition, vec_slice(value, condition))
  z <- vec_assign(x3, condition, value, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, array(rep(c(2, 1, 1, 5), 20), dim = dim(x3)))

  y <- vec_assign(x3_list, condition, vec_slice(value_list, condition))
  z <- vec_assign(x3_list, condition, value_list, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, array(rep(list(2, 1, 1, 5), 20), dim = dim(x3_list)))

  y <- vec_assign(x4, condition, vec_slice(value, condition))
  z <- vec_assign(x4, condition, value, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, array(rep(c(2, 1, 1, 5), 120), dim = dim(x4)))

  y <- vec_assign(x4_list, condition, vec_slice(value_list, condition))
  z <- vec_assign(x4_list, condition, value_list, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, array(rep(list(2, 1, 1, 5), 120), dim = dim(x4_list)))

  # `value` recycling
  value <- 2
  value_list <- as.list(value)

  y <- vec_assign(x1, condition, value)
  z <- vec_assign(x1, condition, value, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, array(rep(c(2, 1, 1, 2), 1), dim = dim(x1)))

  y <- vec_assign(x1_list, condition, value_list)
  z <- vec_assign(x1_list, condition, value_list, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, array(rep(list(2, 1, 1, 2), 1), dim = dim(x1_list)))

  y <- vec_assign(x2, condition, value)
  z <- vec_assign(x2, condition, value, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, array(rep(c(2, 1, 1, 2), 4), dim = dim(x2)))

  y <- vec_assign(x2_list, condition, value_list)
  z <- vec_assign(x2_list, condition, value_list, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, array(rep(list(2, 1, 1, 2), 4), dim = dim(x2_list)))

  y <- vec_assign(x3, condition, value)
  z <- vec_assign(x3, condition, value, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, array(rep(c(2, 1, 1, 2), 20), dim = dim(x3)))

  y <- vec_assign(x3_list, condition, value_list)
  z <- vec_assign(x3_list, condition, value_list, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, array(rep(list(2, 1, 1, 2), 20), dim = dim(x3_list)))

  y <- vec_assign(x4, condition, value)
  z <- vec_assign(x4, condition, value, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, array(rep(c(2, 1, 1, 2), 120), dim = dim(x4)))

  y <- vec_assign(x4_list, condition, value_list)
  z <- vec_assign(x4_list, condition, value_list, slice_value = TRUE)
  expect_identical(y, z)
  expect_identical(y, array(rep(list(2, 1, 1, 2), 120), dim = dim(x4_list)))
})

test_that("atomics can't be assigned in lists", {
  x <- list(NULL)
  expect_error(vec_slice(x, 1) <- 1, class = "vctrs_error_incompatible_type")
  expect_error(vec_assign(x, 1, 2), class = "vctrs_error_incompatible_type")

  expect_error(
    vec_slice(x, 1) <- "foo",
    class = "vctrs_error_incompatible_type"
  )
  expect_error(vec_assign(x, 1, "foo"), class = "vctrs_error_incompatible_type")
})

test_that("Unspecified `NA` vector can be assigned into lists", {
  x <- list(1, 2)
  vec_slice(x, 1) <- NA
  expect_identical(x, list(NULL, 2))
})

test_that("monitoring test - unspecified() can be assigned in lists", {
  x <- list(1, 2)
  expect_error(vec_slice(x, 1) <- unspecified(1), NA)
  expect_equal(x, list(NULL, 2))
})

test_that("can assign and slice-assign data frames", {
  df <- data.frame(x = 1:2)
  df$y <- data.frame(a = 2:1)

  orig <- duplicate(df, shallow = FALSE)

  other <- data.frame(x = 3)
  other$y <- data.frame(a = 3)

  exp <- data.frame(x = int(3, 2))
  exp$y <- data.frame(a = int(3, 1))

  expect_identical(vec_assign(df, 1, other), exp)
  expect_identical(df, orig)

  expect_identical(vec_assign(df, 1, other, slice_value = TRUE), exp)
  expect_identical(df, orig)

  vec_slice(df, 1) <- other
  expect_identical(df, exp)
})

test_that("can assign using logical index", {
  x <- c(2, 1)
  vec_slice(x, TRUE) <- 3
  expect_equal(x, c(3, 3))

  vec_slice(x, c(TRUE, FALSE)) <- 4
  expect_equal(x, c(4, 3))

  expect_snapshot({
    (expect_error(
      vec_assign(x, c(TRUE, FALSE, TRUE), 5),
      class = "vctrs_error_subscript_size"
    ))
  })
  expect_snapshot({
    (expect_error(
      vec_assign(x, c(TRUE, FALSE, TRUE), 5, slice_value = TRUE),
      class = "vctrs_error_subscript_size"
    ))
  })

  expect_snapshot({
    (expect_error(
      vec_assign(mtcars, c(TRUE, FALSE), mtcars[1, ]),
      class = "vctrs_error_subscript_size"
    ))
  })
  expect_snapshot({
    (expect_error(
      vec_assign(mtcars, c(TRUE, FALSE), mtcars[1, ], slice_value = TRUE),
      class = "vctrs_error_subscript_size"
    ))
  })
})

test_that("assign `value` size depends on `slice_value`", {
  x <- c(1, 2, 3)

  # `value` size depends on number of `NA` or `TRUE` values in `i`
  expect_snapshot(error = TRUE, {
    vec_assign(x, c(TRUE, NA, FALSE), c(1, 2, 3))
  })

  # `value` size depends on size of `x`
  expect_snapshot(error = TRUE, {
    vec_assign(x, c(TRUE, NA, FALSE), c(1, 2), slice_value = TRUE)
  })
})

test_that("assign ignores NA in logical subsetting", {
  x <- c(NA, 1, 2)

  expect_equal(vec_assign(x, x > 0, 1), c(NA, 1, 1))
  expect_equal(vec_assign(x, x > 0, 1, slice_value = TRUE), c(NA, 1, 1))

  expect_equal(vec_assign(x, x > 0, c(NA, 2:1)), c(NA, 2, 1))
  expect_equal(
    vec_assign(x, x > 0, c(NA, 2:1), slice_value = TRUE),
    c(NA, 2, 1)
  )
})

test_that("assign with arrays ignores NA in logical subsetting", {
  mat <- as.matrix
  x <- c(NA, 1, 2)
  expect_equal(vec_assign(mat(x), x > 0, 1), mat(c(NA, 1, 1)))
  expect_equal(vec_assign(mat(x), x > 0, c(NA, 2:1)), mat(c(NA, 2, 1)))
})

test_that("assign ignores NA in integer subsetting", {
  x <- 0:2
  expect_equal(vec_assign(x, c(NA, 2:3), 1), c(0, 1, 1))
  expect_equal(vec_assign(x, c(NA, 2:3), c(NA, 2:1)), c(0, 2, 1))
})

test_that("assign with arrays ignores NA in integer subsetting", {
  mat <- as.matrix
  x <- mat(0:2)
  expect_equal(vec_assign(x, c(NA, 2:3), 1), mat(c(0, 1, 1)))
  expect_equal(vec_assign(x, c(NA, 2:3), c(NA, 2:1)), mat(c(0, 2, 1)))
})

test_that("can't modify subset with missing argument", {
  x <- 1:3
  expect_error(vec_slice(x, ) <- 2L)
})

test_that("can modify subset with recycled NA argument", {
  x <- 1:3
  vec_slice(x, NA) <- 2L
  expect_identical(x, 1:3)
})

test_that("can modify subset with recycled TRUE argument", {
  x <- 1:3
  vec_slice(x, TRUE) <- 2L
  expect_identical(x, rep(2L, 3))
})

test_that("can modify subset with recycled FALSE argument", {
  x <- 1:3
  vec_slice(x, FALSE) <- 2L
  expect_identical(x, 1:3)
})

test_that("can modify subset with NULL argument", {
  x <- 1:3
  vec_slice(x, NULL) <- 2L
  expect_identical(x, 1:3)

  x <- vec_assign(x, NULL, 2L)
  expect_identical(x, 1:3)

  x <- vec_assign(x, NULL, 2L, slice_value = TRUE)
  expect_identical(x, 1:3)
})

test_that("can slice-assign with missing indices", {
  # Atomic case
  x <- 1:3
  y <- 4:6
  test <- c(NA, TRUE, FALSE)
  vec_slice(x, test) <- vec_slice(y, test)
  expect_identical(x, int(1, 5, 3))

  # Barrier case
  x <- as.list(1:3)
  y <- as.list(4:6)
  test <- c(NA, TRUE, FALSE)
  vec_slice(x, test) <- vec_slice(y, test)
  expect_identical(x, as.list(int(1, 5, 3)))

  # Atomic array case
  x <- array(1:12, dim = c(3, 2, 2))
  y <- array(13:24, dim = c(3, 2, 2))
  test <- c(NA, TRUE, FALSE)
  vec_slice(x, test) <- vec_slice(y, test)
  expect <- array(
    int(1, 14, 3, 4, 17, 6, 7, 20, 9, 10, 23, 12),
    dim = c(3, 2, 2)
  )
  expect_identical(x, expect)

  # Barrier array case
  x <- array(as.list(1:12), dim = c(3, 2, 2))
  y <- array(as.list(13:24), dim = c(3, 2, 2))
  test <- c(NA, TRUE, FALSE)
  vec_slice(x, test) <- vec_slice(y, test)
  expect <- array(
    as.list(int(1, 14, 3, 4, 17, 6, 7, 20, 9, 10, 23, 12)),
    dim = c(3, 2, 2)
  )
  expect_identical(x, expect)
})

test_that("slice-assign checks vectorness", {
  x <- foobar(list(1))
  expect_error(vec_slice(x, 1) <- 10, class = "vctrs_error_scalar_type")
})

test_that("a coercible RHS is cast to LHS before assignment (#140)", {
  x <- 1:2
  expect_error(vec_slice(x, 1) <- "1", class = "vctrs_error_incompatible_type")

  x <- c("foo", "bar")
  expect_error(vec_slice(x, 1) <- 1, class = "vctrs_error_incompatible_type")

  x <- 1:2
  expect_error(vec_slice(x, 1) <- 3.5, class = "vctrs_error_cast_lossy")

  allow_lossy_cast(vec_slice(x, 1) <- 3.5)
  expect_identical(x, int(3, 2))

  x <- matrix(1:4, 2)
  vec_slice(x, 1) <- matrix(c(FALSE, FALSE), 1)
  expect_identical(x, matrix(int(0, 2, 0, 4), 2))
  expect_error(
    vec_assign(x, 1, matrix(c("", ""), 1)),
    class = "vctrs_error_incompatible_type"
  )
})

test_that("slice-assign takes the proxy", {
  local_proxy()

  x <- new_proxy(1:3)
  y <- new_proxy(20:21)

  vec_slice(x, 2:3) <- y

  expect_identical(proxy_deref(x), int(1, 20, 21))
})

test_that("can use names to assign with a named object", {
  x0 <- c(a = 1, b = 2, c = 3)
  x1 <- c(a = 1, a = 2, a = 3)

  vec_slice(x0, "b") <- 4
  expect_identical(x0, c(a = 1, b = 4, c = 3))

  # Only first is changed
  vec_slice(x1, "a") <- 4
  expect_identical(x1, c(a = 4, a = 2, a = 3))

  x <- c(a = 1, b = 2, c = 3)

  expect_identical(
    vec_assign(x, c("c", "a"), c(4, 5)),
    c(a = 5, b = 2, c = 4)
  )
  # Slices `value` by `i` after matching `i` against `x` names
  expect_identical(
    vec_assign(x, c("c", "a"), c(4, 5, 6), slice_value = TRUE),
    c(a = 4, b = 2, c = 6)
  )

  expect_snapshot(error = TRUE, {
    vec_assign(x, c("c", "a"), c(4, 5, 6))
  })
  expect_snapshot(error = TRUE, {
    vec_assign(x, c("c", "a"), c(4, 5), slice_value = TRUE)
  })
})

test_that("can't use names to assign with an unnamed object", {
  x0 <- 1:3

  expect_error(
    vec_slice(x0, letters[1]) <- 4L,
    "Can't use character names to index an unnamed vector.",
    fixed = TRUE
  )
  expect_error(
    vec_assign(x0, letters[1], 4L, slice_value = TRUE),
    "Can't use character names to index an unnamed vector.",
    fixed = TRUE
  )
  expect_error(
    vec_slice(x0, letters[25:27]) <- 5L,
    "Can't use character names to index an unnamed vector.",
    fixed = TRUE
  )
})

test_that("slice-assign falls back to `[<-` when proxy is not implemented", {
  obj <- foobar(c("foo", "bar", "baz"))
  expect_error(
    vec_slice(obj, 1:2) <- TRUE,
    class = "vctrs_error_incompatible_type"
  )

  vec_slice(obj, 1:2) <- foobar("quux")

  vec_ptype2(foobar(""), foobar(""))
  vec_cast(foobar(""), foobar(""))
  #> Error: Can't cast <vctrs_foobar> to <vctrs_foobar>

  local_methods(
    `[<-.vctrs_foobar` = function(x, i, value) {
      x <- unclass(x)
      x[i] <- "dispatched"
      x
    },
    vec_ptype2.logical.vctrs_foobar = function(...) foobar(""),
    vec_ptype2.vctrs_foobar = function(...) foobar(""),
    vec_cast.vctrs_foobar = function(...) NULL,
    vec_cast.vctrs_foobar.logical = function(x, to, ...) {
      foobar(rep("", length(x)))
    }
  )

  obj <- foobar(c("foo", "bar", "baz"))

  obj2 <- vec_assign(obj, 1:2, TRUE)
  expect_identical(obj2, foobar(c("dispatched", "dispatched", "baz")))

  obj2 <- vec_assign(obj, c(TRUE, FALSE, TRUE), TRUE)
  expect_identical(obj2, foobar(c("dispatched", "bar", "dispatched")))

  # We handle `NA` for the end user
  obj2 <- vec_assign(obj, c(1, NA, 2), TRUE)
  expect_identical(obj2, foobar(c("dispatched", "dispatched", "baz")))
})

test_that("vec_assign() can always assign unspecified values into foreign vector types", {
  obj <- foobar(c("foo", "bar", "baz"))
  expect <- foobar(c(NA, "bar", "baz"))

  expect_identical(vec_assign(obj, 1, NA), expect)
  expect_identical(vec_assign(obj, 1, unspecified(1)), expect)

  expect_identical(
    vec_assign(obj, 1, c(NA, NA, NA), slice_value = TRUE),
    expect
  )
  expect_identical(
    vec_assign(obj, 1, unspecified(3), slice_value = TRUE),
    expect
  )
})

test_that("slice-assign casts to `to` before falling back to `[<-` (#443)", {
  called <- FALSE

  local_methods(
    vec_proxy.vctrs_proxy = proxy_deref,
    vec_ptype2.vctrs_proxy = function(...) NULL,
    vec_ptype2.vctrs_proxy.vctrs_foobar = function(...) new_proxy(NA),
    vec_cast.vctrs_foobar = function(...) NULL,
    vec_cast.vctrs_foobar.vctrs_proxy = function(x, ...) foobar(proxy_deref(x)),
    `[<-.vctrs_foobar` = function(x, i, value) {
      called <<- TRUE
      expect_identical(value, foobar(10))
    }
  )

  x <- foobar(1)
  y <- new_proxy(10)
  vec_slice(x, 1) <- y
  expect_true(called)
})

test_that("index and value are sliced before falling back", {
  # Works around a bug in base R `[<-` before we call it, in particular, related
  # to the fact that `[<-` doesn't allow `NA` in subassign indices, but we do.

  lhs <- foobar(int(1:5))

  # With location vector
  expect_identical(
    vec_assign(lhs, c(NA, 1), foobar(int(6:7))),
    foobar(int(7, 2:5))
  )

  # With location vector and `slice_value = TRUE`
  expect_identical(
    vec_assign(lhs, c(NA, 1), foobar(int(6:10)), slice_value = TRUE),
    foobar(int(6, 2:5))
  )

  # With condition vector
  expect_identical(
    vec_assign(lhs, c(NA, FALSE, TRUE, NA, TRUE), foobar(int(6:9))),
    foobar(int(1, 2, 7, 4, 9))
  )

  # With condition vector and `slice_value = TRUE`
  expect_identical(
    vec_assign(
      lhs,
      c(NA, FALSE, TRUE, NA, TRUE),
      foobar(int(6:10)),
      slice_value = TRUE
    ),
    foobar(int(1, 2, 8, 4, 10))
  )
})

test_that("size 1 value is expected to be handled by the `[<-` fallback", {
  # i.e., we don't pre recycle size 1 value to the size of the index because
  # we expect that most `[<-` fallbacks eventually call base `[<-`, which
  # recycles size 1 value efficiently. See `vec_assign_fallback()`.
  lhs <- foobar(1:4)
  rhs <- foobar(0L)
  exp <- foobar(c(1L, 0L, 3L, 0L))

  expect_identical(vec_assign(lhs, c(2L, 4L), rhs), exp)

  expect_identical(
    vec_assign(lhs, c(FALSE, TRUE, FALSE, TRUE), rhs),
    exp
  )
  expect_identical(
    vec_assign(lhs, c(FALSE, TRUE, FALSE, TRUE), rhs, slice_value = TRUE),
    exp
  )
})

test_that("can assign to data frame", {
  x <- data_frame(x = 1:3)
  y <- data_frame(x = 20)
  expect_identical(vec_assign(x, 2, y), data_frame(x = int(1, 20, 3)))
})

test_that("can assign to data frame with `slice_value`", {
  x <- data_frame(x = 1:4)
  y <- data_frame(x = 21:24)
  i <- c(TRUE, FALSE, NA, TRUE)
  expect_identical(
    vec_assign(x, i, y, slice_value = TRUE),
    data_frame(x = int(21, 2, 3, 24))
  )
})

test_that("can assign to a data frame with matrix columns (#625)", {
  df <- tibble(x = 1:2, y = matrix(1:4, nrow = 2))
  expect_identical(vec_assign(df, 2L, df[1, ]), vec_slice(df, c(1, 1)))
})

test_that("assigning to a factor doesn't produce corrupt levels (#853)", {
  x <- factor(c("a", NA), levels = c("a", "b"))
  value <- factor("b", levels = "b")

  res <- vec_assign(x, 2, value)
  expect_identical(res, factor(c("a", "b")))

  res <- vec_assign(x, 1:2, value)
  expect_identical(res, factor(c("b", "b"), levels = c("a", "b")))
})

test_that("can slice-assign unspecified vectors with default type2 method", {
  local_rational_class()
  x <- rational(1:2, 2:3)
  x[[1]] <- NA
  expect_identical(x, rational(c(NA, 2L), c(NA, 3L)))
})

test_that("`vec_assign()` evaluates arg lazily", {
  expect_silent(vec_assign(1L, 1L, 1L, x_arg = print("oof")))
  expect_silent(vec_assign(1L, 1L, 1L, value_arg = print("oof")))
})

test_that("`vec_assign()` requires recyclable value", {
  expect_snapshot({
    (expect_error(
      vec_assign(1:3, 1:2, 1:3),
      class = "vctrs_error_recycle_incompatible_size"
    ))
  })
  expect_snapshot({
    (expect_error(
      vec_assign(1:3, 1:2, 1:2, slice_value = TRUE),
      class = "vctrs_error_recycle_incompatible_size"
    ))
  })
})

test_that("logical subscripts must match size of indexed vector", {
  expect_snapshot({
    (expect_error(
      vec_assign(1:2, c(TRUE, FALSE, TRUE), 5),
      class = "vctrs_error_subscript_size"
    ))
  })

  expect_snapshot(
    (expect_error(
      vec_assign(mtcars, c(TRUE, FALSE), mtcars[1, ]),
      class = "vctrs_error_subscript_size"
    ))
  )
})

test_that("must assign existing elements", {
  expect_snapshot({
    (expect_error(
      vec_assign(1:3, 5, 10),
      class = "vctrs_error_subscript_oob"
    ))
    (expect_error(
      vec_assign(1:3, "foo", 10),
      "unnamed vector"
    ))
    (expect_error(
      vec_slice(letters, -100) <- "foo",
      class = "vctrs_error_subscript_oob"
    ))
    (expect_error(
      vec_assign(set_names(letters), "foo", "bar"),
      class = "vctrs_error_subscript_oob"
    ))
  })
})

test_that("must assign with proper negative locations", {
  expect_snapshot({
    (expect_error(
      vec_assign(1:3, c(-1, 1), 1:2),
      class = "vctrs_error_subscript_type"
    ))
    (expect_error(
      vec_assign(1:3, c(-1, NA), 1:2),
      class = "vctrs_error_subscript_type"
    ))
  })
})

test_that("`vec_assign()` error args can be overridden", {
  expect_snapshot({
    (expect_error(
      vec_assign(1:2, 1L, "x", x_arg = "foo", value_arg = "bar"),
      class = "vctrs_error_incompatible_type"
    ))
    (expect_error(
      vec_assign(1:2, 1L, 1:2, value_arg = "bar"),
      class = "vctrs_error_recycle_incompatible_size"
    ))
  })
})

test_that("names are not assigned by default", {
  vec_x <- set_names(1:3, letters[1:3])
  vec_y <- c(FOO = 4L)
  vec_out <- c(a = 1L, b = 4L, c = 3L)
  expect_identical(
    vec_assign(vec_x, 2, vec_y),
    vec_out
  )

  df_x <- new_data_frame(list(x = 1:3), row.names = letters[1:3])
  df_y <- new_data_frame(list(x = 4L), row.names = "FOO")
  df_out <- new_data_frame(list(x = c(1L, 4L, 3L)), row.names = letters[1:3])
  expect_identical(
    vec_assign(df_x, 2, df_y),
    df_out
  )

  mat_x <- matrix(1:3, 3, dimnames = list(letters[1:3]))
  mat_y <- matrix(4L, 1, dimnames = list("FOO"))
  mat_out <- matrix(c(1L, 4L, 3L), dimnames = list(letters[1:3]))
  expect_identical(
    vec_assign(mat_x, 2, mat_y),
    mat_out
  )

  nested_x <- new_data_frame(
    list(df = df_x, mat = mat_x, vec = vec_x),
    row.names = c("foo", "bar", "baz")
  )
  nested_y <- new_data_frame(
    list(df = df_y, mat = mat_y, vec = vec_y),
    row.names = c("quux")
  )
  nested_out <- new_data_frame(
    list(df = df_out, mat = mat_out, vec = vec_out),
    row.names = c("foo", "bar", "baz")
  )
  expect_identical(
    vec_assign(nested_x, 2, nested_y),
    nested_out
  )
})

test_that("can optionally assign names", {
  vec_x <- set_names(1:3, letters[1:3])
  vec_y <- c(FOO = 4L)
  vec_out <- c(a = 1L, FOO = 4L, c = 3L)
  expect_identical(
    vec_assign_params(vec_x, 2, vec_y, assign_names = TRUE),
    vec_out
  )

  df_x <- new_data_frame(list(x = 1:3), row.names = letters[1:3])
  df_y <- new_data_frame(list(x = 4L), row.names = "FOO")
  df_out <- new_data_frame(
    list(x = c(1L, 4L, 3L)),
    row.names = c("a", "FOO", "c")
  )
  expect_identical(
    vec_assign_params(df_x, 2, df_y, assign_names = TRUE),
    df_out
  )

  mat_x <- matrix(1:3, 3, dimnames = list(letters[1:3]))
  mat_y <- matrix(4L, 1, dimnames = list("FOO"))
  mat_out <- matrix(c(1L, 4L, 3L), dimnames = list(c("a", "FOO", "c")))
  expect_identical(
    vec_assign_params(mat_x, 2, mat_y, assign_names = TRUE),
    mat_out
  )

  nested_x <- new_data_frame(
    list(df = df_x, mat = mat_x, vec = vec_x),
    row.names = c("foo", "bar", "baz")
  )
  nested_y <- new_data_frame(
    list(df = df_y, mat = mat_y, vec = vec_y),
    row.names = c("quux")
  )
  nested_out <- new_data_frame(
    list(df = df_out, mat = mat_out, vec = vec_out),
    row.names = c("foo", "quux", "baz")
  )

  expect_identical(
    vec_assign_params(nested_x, 2, nested_y, assign_names = TRUE),
    nested_out
  )
})

test_that("can optionally assign names with `slice_value`", {
  vec_x <- set_names(1:3, letters[1:3])
  vec_y <- c(FOO = 4L, BAR = 5L, BAZ = 6L)
  vec_out <- c(a = 1L, BAR = 5L, BAZ = 6L)
  expect_identical(
    vec_assign_params(
      vec_x,
      c(FALSE, TRUE, TRUE),
      vec_y,
      assign_names = TRUE,
      slice_value = TRUE
    ),
    vec_out
  )

  df_x <- new_data_frame(list(x = 1:3), row.names = letters[1:3])
  df_y <- new_data_frame(list(x = 4:6), row.names = c("FOO", "BAR", "BAZ"))
  df_out <- new_data_frame(
    list(x = c(1L, 5L, 6L)),
    row.names = c("a", "BAR", "BAZ")
  )
  expect_identical(
    vec_assign_params(
      df_x,
      c(FALSE, TRUE, TRUE),
      df_y,
      assign_names = TRUE,
      slice_value = TRUE
    ),
    df_out
  )

  mat_x <- matrix(1:3, 3, dimnames = list(letters[1:3]))
  mat_y <- matrix(4:6, 3, dimnames = list(c("FOO", "BAR", "BAZ")))
  mat_out <- matrix(c(1L, 5L, 6L), dimnames = list(c("a", "BAR", "BAZ")))
  expect_identical(
    vec_assign_params(
      mat_x,
      c(FALSE, TRUE, TRUE),
      mat_y,
      assign_names = TRUE,
      slice_value = TRUE
    ),
    mat_out
  )

  nested_x <- new_data_frame(
    list(df = df_x, mat = mat_x, vec = vec_x),
    row.names = c("foo", "bar", "baz")
  )
  nested_y <- new_data_frame(
    list(df = df_y, mat = mat_y, vec = vec_y),
    row.names = c(c("x", "y", "z"))
  )
  nested_out <- new_data_frame(
    list(df = df_out, mat = mat_out, vec = vec_out),
    row.names = c("foo", "y", "z")
  )

  expect_identical(
    vec_assign_params(
      nested_x,
      c(FALSE, TRUE, TRUE),
      nested_y,
      assign_names = TRUE,
      slice_value = TRUE
    ),
    nested_out
  )
})

test_that("can optionally assign names (OO case)", {
  # In case upstream attributes handling changes
  skip_on_cran()

  # `set_names()` must be on the inside, otherwise the POSIXlt object
  # gets a `balanced` attribute of `NA`
  oo_x <- as_posixlt(set_names(
    c("2020-01-01", "2020-01-02", "2020-01-03"),
    letters[1:3]
  ))
  oo_y <- as_posixlt(c(FOO = "2020-01-04"))
  oo_out <- as_posixlt(c(
    a = "2020-01-01",
    FOO = "2020-01-04",
    c = "2020-01-03"
  ))
  expect_identical(
    vec_assign_params(oo_x, 2, oo_y, assign_names = TRUE),
    oo_out
  )

  nested_x <- new_data_frame(
    list(oo = oo_x),
    row.names = c("foo", "bar", "baz")
  )
  nested_y <- new_data_frame(list(oo = oo_y), row.names = c("quux"))
  nested_out <- new_data_frame(
    list(oo = oo_out),
    row.names = c("foo", "quux", "baz")
  )

  expect_identical(
    vec_assign_params(nested_x, 2, nested_y, assign_names = TRUE),
    nested_out
  )
})

test_that("assigning names clears existing names even if the new value doesn't have any (#2019)", {
  x <- c(a = 1, b = 2)

  # Keeps existing names
  expect_identical(
    vec_assign_params(x, 1L, 0),
    c(a = 0, b = 2)
  )
  expect_identical(
    vec_assign_params(x, 1L, c(c = 0)),
    c(a = 0, b = 2)
  )

  # Clears or replaces names
  expect_identical(
    vec_assign_params(x, 1L, 0, assign_names = TRUE),
    set_names(c(0, 2), c("", "b"))
  )
  expect_identical(
    vec_assign_params(x, 1L, c(c = 0), assign_names = TRUE),
    set_names(c(0, 2), c("c", "b"))
  )
})

test_that("assignment requires that the value proxy is the same type as the output proxy", {
  x <- foobar(1)
  y <- foobar("a")

  local_foobar_proxy()
  local_methods(
    vec_cast.vctrs_foobar.vctrs_foobar = function(x, to, ...) x
  )

  expect_error(
    vec_assign(x, 1, y),
    "`double` incompatible with `value` proxy of type `character`"
  )
})

test_that("assignment allows a df `value`'s column to be a different type than its proxy (#1082)", {
  x <- new_data_frame(list(x = foobar(1)))
  y <- new_data_frame(list(x = foobar(2)))

  local_methods(
    # proxying foobar wraps it in a 1 col df
    vec_proxy.vctrs_foobar = function(x, ...) {
      attributes(x) <- NULL
      new_data_frame(list(vec = x))
    },
    # restoring extracts the column
    vec_restore.vctrs_foobar = function(x, to, ...) {
      foobar(x$vec)
    },
    vec_ptype2.vctrs_foobar.vctrs_foobar = function(x, y, ...) x
  )

  expect1 <- new_data_frame(list(x = foobar(c(1, 1))))
  expect2 <- new_data_frame(list(x = foobar(2)))

  expect_identical(vec_rbind(x, x), expect1)
  expect_identical(vec_assign(x, 1, y), expect2)
})

test_that("monitoring: assignment to a data frame with unshared columns doesn't overwrite (#986)", {
  # We now require R >= 4.0.0, so this test no longer needs the < 4.0.0
  # branches, but we keep it around for historical reference

  x <- new_df_unshared_col()
  value <- new_data_frame(list(x = 2))
  expect <- new_data_frame(list(x = 1L))

  # - On R < 4.0.0, the NAMED value of the column is 0.
  # - On R >= 4.0.0, the refcnt of the column is 1 from the call to
  #   `SET_VECTOR_ELT()` in `new_df_unshared_col()`.
  expect_false(maybe_shared_col(x, 1L))

  new <- vec_assign(x, 1, value)

  # On R < 4.0.0, `vec_assign()` shallow duplicates `x`, which recursively
  # bumps the NAMED-ness of each column of `x` to the max value of 7 by
  # calling `ENSURE_NAMEDMAX()` on it. So the columns of `x` are all considered
  # shared from that.

  # On R >= 4.0.0, references are tracked more precisely.
  # - `new_df_unshared_col()` calls `SET_VECTOR_ELT()` when setting the
  #   column into `x`, bumping the column's namedness to 1.
  # - Then, at the start of `df_assign()`, `x` is shallow duplicated and
  #   assigned to `out`. This calls `ENSURE_NAMEDMAX()` on each column,
  #   however this does nothing on R 4.0.0. The refcnt of each column is instead
  #   incremented by 1 by calls to `SET_VECTOR_ELT()` in `duplicate1()`.
  #   So now it is at 2.
  # - But then in `df_assign()` we use `SET_VECTOR_ELT()` on `out`, overwriting
  #   each column. This actually decrements the refcnt on the value that was
  #   in `out` before the column was overwritten. The column of `out` that it
  #   decrements the refcnt for is the same SEXP as that column in `x`, so now
  #   it is back to 1, and it is not considered shared.

  if (getRversion() >= "4.0.0") {
    expect_false(maybe_shared_col(x, 1L))
  } else {
    expect_true(maybe_shared_col(x, 1L))
  }

  # Expect no changes to `x`!
  expect_identical(x, expect)
})

test_that("monitoring: assignment to atomic vectors doesn't modify by reference", {
  x <- c(1, 2, 3)
  expect <- c(1, 2, 3)

  vec_assign(x, 2, 3)

  expect_identical(x, expect)
})

test_that("monitoring: assignment to POSIXlt doesn't modify by reference (#1951)", {
  original <- as.POSIXlt("2020-11-01")
  expect_original <- as.POSIXlt("2020-11-01")

  value <- as.POSIXlt("2020-12-02")

  expect <- as.POSIXlt("2020-12-02")
  actual <- vec_assign(original, 1, value)
  expect_identical(expect, actual)

  expect_identical(original, expect_original)
})

test_that("monitoring: assignment to `vctrs_rcrd` doesn't modify by reference (#1951)", {
  original <- new_rcrd(list(x = 1:5))
  expect_original <- new_rcrd(list(x = 1:5))

  value <- new_rcrd(list(x = 0L))

  expect <- new_rcrd(list(x = c(0L, 2:5)))
  actual <- vec_assign(original, 1, value)
  expect_identical(expect, actual)

  expect_identical(original, expect_original)
})

# vec_assign + compact_seq -------------------------------------------------

test_that("can assign base vectors with compact seqs", {
  # `start` is 0-based
  start <- 1L
  size <- 2L
  increasing <- TRUE

  x <- c(FALSE, FALSE, FALSE)
  value <- c(TRUE, NA, TRUE)
  value_sliced <- vec_slice_seq(value, start, size, increasing)
  y <- vec_assign_seq(x, value_sliced, start, size, increasing)
  expect_identical(y, c(FALSE, NA, TRUE))
  expect_identical(
    vec_assign_seq(x, value, start, size, increasing, slice_value = TRUE),
    y
  )

  x <- c(1L, 2L, 3L)
  value <- c(4L, 5L, 6L)
  value_sliced <- vec_slice_seq(value, start, size, increasing)
  y <- vec_assign_seq(x, value_sliced, start, size, increasing)
  expect_identical(y, c(1L, 5L, 6L))
  expect_identical(
    vec_assign_seq(x, value, start, size, increasing, slice_value = TRUE),
    y
  )

  x <- c(1, 2, 3)
  value <- c(4, 5, 6)
  value_sliced <- vec_slice_seq(value, start, size, increasing)
  y <- vec_assign_seq(x, value_sliced, start, size, increasing)
  expect_identical(y, c(1, 5, 6))
  expect_identical(
    vec_assign_seq(x, value, start, size, increasing, slice_value = TRUE),
    y
  )

  x <- c(1i, 2i, 3i)
  value <- c(4i, 5i, 6i)
  value_sliced <- vec_slice_seq(value, start, size, increasing)
  y <- vec_assign_seq(x, value_sliced, start, size, increasing)
  expect_identical(y, c(1i, 5i, 6i))
  expect_identical(
    vec_assign_seq(x, value, start, size, increasing, slice_value = TRUE),
    y
  )

  x <- c("1", "2", "3")
  value <- c("4", "5", "6")
  value_sliced <- vec_slice_seq(value, start, size, increasing)
  y <- vec_assign_seq(x, value_sliced, start, size, increasing)
  expect_identical(y, c("1", "5", "6"))
  expect_identical(
    vec_assign_seq(x, value, start, size, increasing, slice_value = TRUE),
    y
  )

  x <- raw2(1, 2, 3)
  value <- raw2(4, 5, 6)
  value_sliced <- vec_slice_seq(value, start, size, increasing)
  y <- vec_assign_seq(x, value_sliced, start, size, increasing)
  expect_identical(y, raw2(1, 5, 6))
  expect_identical(
    vec_assign_seq(x, value, start, size, increasing, slice_value = TRUE),
    y
  )

  x <- list(1, 2, 3)
  value <- list(4, 5, 6)
  value_sliced <- vec_slice_seq(value, start, size, increasing)
  y <- vec_assign_seq(x, value_sliced, start, size, increasing)
  expect_identical(y, list(1, 5, 6))
  expect_identical(
    vec_assign_seq(x, value, start, size, increasing, slice_value = TRUE),
    y
  )
})

test_that("can assign shaped base vectors with compact seqs", {
  # `start` is 0-based
  start <- 1L
  size <- 2L
  increasing <- TRUE
  mat <- as.matrix

  x <- mat(c(FALSE, FALSE, FALSE))
  value <- mat(c(TRUE, NA, TRUE))
  value_sliced <- vec_slice_seq(value, start, size, increasing)
  y <- vec_assign_seq(x, value_sliced, start, size, increasing)
  expect_identical(y, mat(c(FALSE, NA, TRUE)))
  expect_identical(
    vec_assign_seq(x, value, start, size, increasing, slice_value = TRUE),
    y
  )

  x <- mat(c(1L, 2L, 3L))
  value <- mat(c(4L, 5L, 6L))
  value_sliced <- vec_slice_seq(value, start, size, increasing)
  y <- vec_assign_seq(x, value_sliced, start, size, increasing)
  expect_identical(y, mat(c(1L, 5L, 6L)))
  expect_identical(
    vec_assign_seq(x, value, start, size, increasing, slice_value = TRUE),
    y
  )

  x <- mat(c(1, 2, 3))
  value <- mat(c(4, 5, 6))
  value_sliced <- vec_slice_seq(value, start, size, increasing)
  y <- vec_assign_seq(x, value_sliced, start, size, increasing)
  expect_identical(y, mat(c(1, 5, 6)))
  expect_identical(
    vec_assign_seq(x, value, start, size, increasing, slice_value = TRUE),
    y
  )

  x <- mat(c(1i, 2i, 3i))
  value <- mat(c(4i, 5i, 6i))
  value_sliced <- vec_slice_seq(value, start, size, increasing)
  y <- vec_assign_seq(x, value_sliced, start, size, increasing)
  expect_identical(y, mat(c(1i, 5i, 6i)))
  expect_identical(
    vec_assign_seq(x, value, start, size, increasing, slice_value = TRUE),
    y
  )

  x <- mat(c("1", "2", "3"))
  value <- mat(c("4", "5", "6"))
  value_sliced <- vec_slice_seq(value, start, size, increasing)
  y <- vec_assign_seq(x, value_sliced, start, size, increasing)
  expect_identical(y, mat(c("1", "5", "6")))
  expect_identical(
    vec_assign_seq(x, value, start, size, increasing, slice_value = TRUE),
    y
  )

  x <- mat(raw2(1, 2, 3))
  value <- mat(raw2(4, 5, 6))
  value_sliced <- vec_slice_seq(value, start, size, increasing)
  y <- vec_assign_seq(x, value_sliced, start, size, increasing)
  expect_identical(y, mat(raw2(1, 5, 6)))
  expect_identical(
    vec_assign_seq(x, value, start, size, increasing, slice_value = TRUE),
    y
  )

  x <- mat(list(1, 2, 3))
  value <- mat(list(4, 5, 6))
  value_sliced <- vec_slice_seq(value, start, size, increasing)
  y <- vec_assign_seq(x, value_sliced, start, size, increasing)
  expect_identical(y, mat(list(1, 5, 6)))
  expect_identical(
    vec_assign_seq(x, value, start, size, increasing, slice_value = TRUE),
    y
  )
})

test_that("can assign shaped base vectors with compact seqs and recycled `value`", {
  # `start` is 0-based
  start <- 1L
  size <- 2L
  increasing <- TRUE
  mat <- as.matrix
  expect_identical(
    vec_assign_seq(mat(lgl(1, 0, 1)), NA, start, size, increasing),
    mat(lgl(1, NA, NA))
  )
  expect_identical(
    vec_assign_seq(mat(int(1, 2, 3)), NA, start, size, increasing),
    mat(int(1, NA, NA))
  )
  expect_identical(
    vec_assign_seq(mat(dbl(1, 2, 3)), NA, start, size, increasing),
    mat(dbl(1, NA, NA))
  )
  expect_identical(
    vec_assign_seq(mat(cpl2(1, 2, 3)), NA, start, size, increasing),
    mat(cpl2(1, NA, NA))
  )
  expect_identical(
    vec_assign_seq(mat(chr("1", "2", "3")), NA, start, size, increasing),
    mat(chr("1", NA, NA))
  )
  expect_identical(
    vec_assign_seq(mat(raw2(1, 2, 3)), raw2(1), start, size, increasing),
    mat(raw2(1, 1, 1))
  )
  expect_identical(
    vec_assign_seq(mat(list(1, 2, 3)), NA, start, size, increasing),
    mat(list(1, NULL, NULL))
  )
})

test_that("can assign shaped base vectors with decreasing compact seqs and recycled `value`", {
  # `start` is 0-based
  start <- 2L
  size <- 2L
  increasing <- FALSE
  mat <- as.matrix
  expect_identical(
    vec_assign_seq(mat(lgl(1, 0, 1)), NA, start, size, increasing),
    mat(lgl(1, NA, NA))
  )
  expect_identical(
    vec_assign_seq(mat(int(1, 2, 3)), NA, start, size, increasing),
    mat(int(1, NA, NA))
  )
  expect_identical(
    vec_assign_seq(mat(dbl(1, 2, 3)), NA, start, size, increasing),
    mat(dbl(1, NA, NA))
  )
  expect_identical(
    vec_assign_seq(mat(cpl2(1, 2, 3)), NA, start, size, increasing),
    mat(cpl2(1, NA, NA))
  )
  expect_identical(
    vec_assign_seq(mat(chr("1", "2", "3")), NA, start, size, increasing),
    mat(chr("1", NA, NA))
  )
  expect_identical(
    vec_assign_seq(mat(raw2(1, 2, 3)), raw2(1), start, size, increasing),
    mat(raw2(1, 1, 1))
  )
  expect_identical(
    vec_assign_seq(mat(list(1, 2, 3)), NA, start, size, increasing),
    mat(list(1, NULL, NULL))
  )
})

test_that("can assign shaped base vectors with size 0 compact seqs", {
  # `start` is 0-based
  start <- 1L
  size <- 0L
  increasing <- TRUE
  mat <- as.matrix

  expect_identical(
    vec_assign_seq(mat(lgl(1, 0, 1)), NA, start, size, increasing),
    mat(mat(lgl(1, 0, 1)))
  )
  expect_identical(
    vec_assign_seq(mat(int(1, 2, 3)), NA, start, size, increasing),
    mat(int(1, 2, 3))
  )
  expect_identical(
    vec_assign_seq(mat(dbl(1, 2, 3)), NA, start, size, increasing),
    mat(dbl(1, 2, 3))
  )
  expect_identical(
    vec_assign_seq(mat(cpl(1, 2, 3)), NA, start, size, increasing),
    mat(cpl(1, 2, 3))
  )
  expect_identical(
    vec_assign_seq(mat(chr("1", "2", "3")), NA, start, size, increasing),
    mat(chr("1", "2", "3"))
  )
  expect_identical(
    vec_assign_seq(mat(raw2(1, 2, 3)), raw2(1), start, size, increasing),
    mat(raw2(1, 2, 3))
  )
  expect_identical(
    vec_assign_seq(mat(list(1, 2, 3)), NA, start, size, increasing),
    mat(list(1, 2, 3))
  )
})

test_that("can assign object of any dimensionality with compact seqs", {
  x1 <- ones(3)
  x2 <- ones(3, 4)
  x3 <- ones(3, 4, 5)
  x4 <- ones(3, 4, 5, 6)

  # `start` is 0-based
  start <- 0L
  size <- 2L
  increasing <- TRUE
  mat <- as.matrix

  expect_identical(
    vec_assign_seq(x1, 2, start, size, increasing),
    array(rep(c(2, 2, 1), 1), dim = 3)
  )
  expect_identical(
    vec_assign_seq(x2, 2, start, size, increasing),
    array(rep(c(2, 2, 1), 4), dim = c(3, 4))
  )
  expect_identical(
    vec_assign_seq(x3, 2, start, size, increasing),
    array(rep(c(2, 2, 1), 20), dim = c(3, 4, 5))
  )
  expect_identical(
    vec_assign_seq(x4, 2, start, size, increasing),
    array(rep(c(2, 2, 1), 120), dim = c(3, 4, 5, 6))
  )
})

# vec_assign + compact_condition -----------------------------------------------

test_that("can assign base vectors with compact conditions", {
  i <- c(FALSE, TRUE, TRUE)
  i_compact <- as_compact_condition(i)

  x <- c(FALSE, FALSE, FALSE)
  value <- c(TRUE, NA, TRUE)
  value_sliced <- vec_slice(value, i)
  y <- vec_assign_compact_condition(x, i_compact, value_sliced)
  expect_identical(y, c(FALSE, NA, TRUE))
  expect_identical(
    vec_assign_compact_condition(x, i_compact, value, slice_value = TRUE),
    y
  )

  x <- c(1L, 2L, 3L)
  value <- c(4L, 5L, 6L)
  value_sliced <- vec_slice(value, i)
  y <- vec_assign_compact_condition(x, i_compact, value_sliced)
  expect_identical(y, c(1L, 5L, 6L))
  expect_identical(
    vec_assign_compact_condition(x, i_compact, value, slice_value = TRUE),
    y
  )

  x <- c(1, 2, 3)
  value <- c(4, 5, 6)
  value_sliced <- vec_slice(value, i)
  y <- vec_assign_compact_condition(x, i_compact, value_sliced)
  expect_identical(y, c(1, 5, 6))
  expect_identical(
    vec_assign_compact_condition(x, i_compact, value, slice_value = TRUE),
    y
  )

  x <- c(1i, 2i, 3i)
  value <- c(4i, 5i, 6i)
  value_sliced <- vec_slice(value, i)
  y <- vec_assign_compact_condition(x, i_compact, value_sliced)
  expect_identical(y, c(1i, 5i, 6i))
  expect_identical(
    vec_assign_compact_condition(x, i_compact, value, slice_value = TRUE),
    y
  )

  x <- c("1", "2", "3")
  value <- c("4", "5", "6")
  value_sliced <- vec_slice(value, i)
  y <- vec_assign_compact_condition(x, i_compact, value_sliced)
  expect_identical(y, c("1", "5", "6"))
  expect_identical(
    vec_assign_compact_condition(x, i_compact, value, slice_value = TRUE),
    y
  )

  x <- raw2(1, 2, 3)
  value <- raw2(4, 5, 6)
  value_sliced <- vec_slice(value, i)
  y <- vec_assign_compact_condition(x, i_compact, value_sliced)
  expect_identical(y, raw2(1, 5, 6))
  expect_identical(
    vec_assign_compact_condition(x, i_compact, value, slice_value = TRUE),
    y
  )

  x <- list(1, 2, 3)
  value <- list(4, 5, 6)
  value_sliced <- vec_slice(value, i)
  y <- vec_assign_compact_condition(x, i_compact, value_sliced)
  expect_identical(y, list(1, 5, 6))
  expect_identical(
    vec_assign_compact_condition(x, i_compact, value, slice_value = TRUE),
    y
  )
})

test_that("can assign shaped base vectors with compact conditions", {
  i <- c(FALSE, TRUE, TRUE)
  i_compact <- as_compact_condition(i)

  mat <- as.matrix

  x <- mat(c(FALSE, FALSE, FALSE))
  value <- mat(c(TRUE, NA, TRUE))
  value_sliced <- vec_slice(value, i)
  y <- vec_assign_compact_condition(x, i_compact, value_sliced)
  expect_identical(y, mat(c(FALSE, NA, TRUE)))
  expect_identical(
    vec_assign_compact_condition(x, i_compact, value, slice_value = TRUE),
    y
  )

  x <- mat(c(1L, 2L, 3L))
  value <- mat(c(4L, 5L, 6L))
  value_sliced <- vec_slice(value, i)
  y <- vec_assign_compact_condition(x, i_compact, value_sliced)
  expect_identical(y, mat(c(1L, 5L, 6L)))
  expect_identical(
    vec_assign_compact_condition(x, i_compact, value, slice_value = TRUE),
    y
  )

  x <- mat(c(1, 2, 3))
  value <- mat(c(4, 5, 6))
  value_sliced <- vec_slice(value, i)
  y <- vec_assign_compact_condition(x, i_compact, value_sliced)
  expect_identical(y, mat(c(1, 5, 6)))
  expect_identical(
    vec_assign_compact_condition(x, i_compact, value, slice_value = TRUE),
    y
  )

  x <- mat(c(1i, 2i, 3i))
  value <- mat(c(4i, 5i, 6i))
  value_sliced <- vec_slice(value, i)
  y <- vec_assign_compact_condition(x, i_compact, value_sliced)
  expect_identical(y, mat(c(1i, 5i, 6i)))
  expect_identical(
    vec_assign_compact_condition(x, i_compact, value, slice_value = TRUE),
    y
  )

  x <- mat(c("1", "2", "3"))
  value <- mat(c("4", "5", "6"))
  value_sliced <- vec_slice(value, i)
  y <- vec_assign_compact_condition(x, i_compact, value_sliced)
  expect_identical(y, mat(c("1", "5", "6")))
  expect_identical(
    vec_assign_compact_condition(x, i_compact, value, slice_value = TRUE),
    y
  )

  x <- mat(raw2(1, 2, 3))
  value <- mat(raw2(4, 5, 6))
  value_sliced <- vec_slice(value, i)
  y <- vec_assign_compact_condition(x, i_compact, value_sliced)
  expect_identical(y, mat(raw2(1, 5, 6)))
  expect_identical(
    vec_assign_compact_condition(x, i_compact, value, slice_value = TRUE),
    y
  )

  x <- mat(list(1, 2, 3))
  value <- mat(list(4, 5, 6))
  value_sliced <- vec_slice(value, i)
  y <- vec_assign_compact_condition(x, i_compact, value_sliced)
  expect_identical(y, mat(list(1, 5, 6)))
  expect_identical(
    vec_assign_compact_condition(x, i_compact, value, slice_value = TRUE),
    y
  )
})

test_that("can assign shaped base vectors with compact conditions and recycled `value`", {
  i <- as_compact_condition(c(FALSE, TRUE, TRUE))
  mat <- as.matrix

  expect_identical(
    vec_assign_compact_condition(mat(lgl(1, 0, 1)), i, NA),
    mat(lgl(1, NA, NA))
  )
  expect_identical(
    vec_assign_compact_condition(mat(int(1, 2, 3)), i, NA),
    mat(int(1, NA, NA))
  )
  expect_identical(
    vec_assign_compact_condition(mat(dbl(1, 2, 3)), i, NA),
    mat(dbl(1, NA, NA))
  )
  expect_identical(
    vec_assign_compact_condition(mat(cpl2(1, 2, 3)), i, NA),
    mat(cpl2(1, NA, NA))
  )
  expect_identical(
    vec_assign_compact_condition(mat(chr("1", "2", "3")), i, NA),
    mat(chr("1", NA, NA))
  )
  expect_identical(
    vec_assign_compact_condition(mat(raw2(1, 2, 3)), i, raw2(1)),
    mat(raw2(1, 1, 1))
  )
  expect_identical(
    vec_assign_compact_condition(mat(list(1, 2, 3)), i, NA),
    mat(list(1, NULL, NULL))
  )
})

test_that("can assign shaped base vectors with size 0 compact conditions", {
  i <- as_compact_condition(logical())
  mat <- as.matrix

  expect_identical(
    vec_assign_compact_condition(mat(lgl(1, 0, 1)), i, NA),
    mat(mat(lgl(1, 0, 1)))
  )
  expect_identical(
    vec_assign_compact_condition(mat(int(1, 2, 3)), i, NA),
    mat(int(1, 2, 3))
  )
  expect_identical(
    vec_assign_compact_condition(mat(dbl(1, 2, 3)), i, NA),
    mat(dbl(1, 2, 3))
  )
  expect_identical(
    vec_assign_compact_condition(mat(cpl(1, 2, 3)), i, NA),
    mat(cpl(1, 2, 3))
  )
  expect_identical(
    vec_assign_compact_condition(mat(chr("1", "2", "3")), i, NA),
    mat(chr("1", "2", "3"))
  )
  expect_identical(
    vec_assign_compact_condition(mat(raw2(1, 2, 3)), i, raw2(1)),
    mat(raw2(1, 2, 3))
  )
  expect_identical(
    vec_assign_compact_condition(mat(list(1, 2, 3)), i, NA),
    mat(list(1, 2, 3))
  )
})

test_that("can assign object of any dimensionality with compact conditions", {
  x1 <- ones(3)
  x2 <- ones(3, 4)
  x3 <- ones(3, 4, 5)
  x4 <- ones(3, 4, 5, 6)

  i <- as_compact_condition(c(TRUE, TRUE, FALSE))

  expect_identical(
    vec_assign_compact_condition(x1, i, 2),
    array(rep(c(2, 2, 1), 1), dim = 3)
  )
  expect_identical(
    vec_assign_compact_condition(x2, i, 2),
    array(rep(c(2, 2, 1), 4), dim = c(3, 4))
  )
  expect_identical(
    vec_assign_compact_condition(x3, i, 2),
    array(rep(c(2, 2, 1), 20), dim = c(3, 4, 5))
  )
  expect_identical(
    vec_assign_compact_condition(x4, i, 2),
    array(rep(c(2, 2, 1), 120), dim = c(3, 4, 5, 6))
  )
})
