r-lib / vctrs

Generic programming with typed R vectors

Home Page:https://vctrs.r-lib.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

`vec_c()` should probably cast `xs` to a common type before handing off to `df_c_fallback()`

DavisVaughan opened this issue · comments

From tidyverse/tidyr#1507

Also ensure we fix it in list_unchop() too!

See the 1970-01-02?

library(tibble)
library(data.table)
library(vctrs)

xs <- list(
  tibble(x = as.IDate('2012-01-01')),
  tibble(y = 1)
)

vec_c(!!!xs)
#> # A tibble: 2 × 2
#>   x              y
#>   <IDate>    <dbl>
#> 1 2012-01-01    NA
#> 2 1970-01-02     1

# fallback class, so actual combination for this `x` column
# is delayed until `df_c_fallback()`
vctrs:::vec_ptype_common_fallback(!!!xs)
#> # A tibble: 0 × 2
#> # ℹ 2 variables: x <vct:::__>, y <dbl>

# but `df_c_fallback()` gets the _original_ `xs`, not the `xs` after they
# have been cast to a common type! so when it `list_pluck()`s out the 1st column,
# it takes `xs[[1]][[1]]` and `xs[[2]][[1]]` (i.e. the x column of the 1st
# element and the y column of the 2nd element).

# you can prove that it works if you go ahead and pre cast them to a common type
vec_c(!!!vec_cast_common(!!!xs))
#> # A tibble: 2 × 2
#>   x              y
#>   <IDate>    <dbl>
#> 1 2012-01-01    NA
#> 2 NA             1

You can break it further with list_unchop(), since recycling of x isn't carried over either

library(tibble)
library(data.table)
library(vctrs)
#> 
#> Attaching package: 'vctrs'
#> The following object is masked from 'package:tibble':
#> 
#>     data_frame

xs <- list(
  tibble(x = as.IDate('2012-01-01')),
  tibble(y = 1)
)

list_unchop(xs, indices = list(1, 2:3))
#> Error in `list_unchop()`:
#> ! `c()` method returned a vector of unexpected size 2 instead of 3.
#> ℹ In file 'c.c' at line 414.
#> ℹ This is an internal error that was detected in the vctrs package.
#>   Please report it at <https://github.com/r-lib/vctrs/issues> with a reprex (<https://tidyverse.org/help/>) and the full backtrace.
#> Backtrace:
#>     ▆
#>  1. ├─vctrs::list_unchop(xs, indices = list(1, 2:3))
#>  2. └─rlang:::stop_internal_c_lib(...) at vctrs/R/slice-chop.R:164:3
#>  3.   └─rlang::abort(message, call = call, .internal = TRUE, .frame = frame)

Created on 2023-11-02 with reprex v2.0.2

I am fairly certain we purposefully do not call vec_ptype_common() and vec_recycle_common() in this function, I think to avoid copying xs if possible, so I don't think switching to those is a good solution. I imagine we should do some recursive pre check to see if we have any fallback classes, and if so then we put each x from the core assignment loop into a list after casting and recycling it, then we pass that list to df_c_fallback()