Hacker News new | ask | show | jobs
by LegionMammal978 856 days ago
I think there is a real difference: in the static case, the compiler can just recurse into the type definition at any point, compute fully-baked sizes and offsets, and cache them for the rest of the compilation. But in the dynamic case, you end up with the novel dataflow of types that depend on runtime values, and more machinery is necessary to track those dependencies.

Of course, this runtime tracking has always been necessary for C99 VLA support, but I can easily see how it would be surprising for someone not deeply familiar with VLAs, especially given how the naive mental model of "T array[n]; is just syntax sugar for T *array = alloca(n * sizeof(T));" is sufficient for almost all of their uses in existing code. (In any case, it's obviously not "creating an object on the heap" that's the unusual part here!)

1 comments

> more machinery is necessary to track those dependencies.

Well, is it much more machinery? IIRC doing

    void f(size_t n) {
        int x[n];
        n += 1;
        ...
does not resize x, so there is no dataflow dependency or rather, x depends on a hidden const variable so no additional dataflow analysis necessary.
Yet

  void f(size_t n, int cond) {
      if (cond) { n += 1; }
      int x[n];
      ...
does resize x depending on the value of cond, so the size can't necessarily be known until the point where the type (int[n] in this case) is named.

Also, the compiler has to make sure it keeps around implicit locals to store the variable layouts, so that code like

  void f(size_t n) {
      typedef int array[n];
      n += 1;
      array x;
      ...
functions as specified. This kind of pattern is one of the bigger things setting the feature apart from just "syntax sugar for alloca()".