LINUX.ORG.RU

История изменений

Исправление vbr, (текущая версия) :

А как же сишное скрепное «никакого неявно выполняемого кода»?

Согласен, это нарушение этого принципа. С другой стороны функционал явно полезный и как сохранить пользу, не нарушая данный принцип - вопрос интересный…

Попробую на примере сформулировать проблему и подумать.

Как сейчас пишут:

int f(int x) {
  char *s = malloc(1);

  if (x == 0) {
    free(s);
    return 0;
  }

  free(s);
  return 1;
}

Как предлагается:

void f(int x) {
  char *s = malloc(1);
  defer free(s);

  if (x == 0) {
    // free(s);
    return 0;
  }

  // free(s);
  return 1;
}

Видим, что перед return неявно вставляется free(s).

Как этот код пишут «опытные» сишники:

int f(int x) {
  int result;

  char *s = malloc(1);

  if (x == 0) {
    result = 0;
    goto end;
  }

  result = 1;

end:
  free(s);

  return result;
}

У обоих подходов без defer есть очевидная проблема - никто не заставляет писать goto end. Можно написать return 0 и ничего не освободится.

Т.е. можно сформулировать проблему - какой синтаксис придумать, чтобы с одной стороны не было неявного выполнения кода, с другой стороны компилятор заставлял бы освобождать ресурс.

Можно, к примеру, такой подход использовать:

int f(int x) {
  int result;

  char *s = malloc(1);
  must_visit free_s;

  if (x == 0) {
    result = 0;
    goto free_s;
  }

  result = 1;

free_s:
  free(s);

  return result;
}

По сути этот код идентичен предыдущему. Всё на 100% явно, никакого неявного кода или неявных переходов. Но тут есть конструкция must_visit <label>. По задумке эта конструкция заставляет компилятор верифицировать, что все пути выполнения программы после этой конструкции достигают указанной метки. Если написать return 0, то компилятор увидит, что в этом случае метка free_s будет недостижима и выдаст ошибку компиляции.

Возможно этот подход был бы более практичным с точки зрения адаптации существующего кода. Хотя, безусловно, defer выглядит на порядок «симпатичней». Наверное иногда принципами лучше поступаться.

Исходная версия vbr, :

А как же сишное скрепное «никакого неявно выполняемого кода»?

Согласен, это нарушение этого принципа. С другой стороны функционал явно полезный и как сохранить пользу, не нарушая данный принцип - вопрос интересный…

Попробую на примере сформулировать проблему и подумать.

Как сейчас пишут:

int f(int x) {
  char *s = malloc(1);

  if (x == 0) {
    free(s);
    return 0;
  }

  free(s);
  return 1;
}

Как предлагается:

void f(int x) {
  char *s = malloc(1);
  defer free(s);

  if (x == 0) {
    // free(s);
    return 0;
  }

  // free(s);
  return 1;
}

Видим, что перед return неявно вставляется free(s).

Как этот код пишут «опытные» сишники:

int f(int x) {
  int result;

  char *s = malloc(1);

  if (x == 0) {
    result = 0;
    goto end;
  }

  result = 1;

end:
  free(s);

  return result;
}

У обоих подходов без defer есть очевидная проблема - никто не заставляет писать goto end. Можно написать return 0 и ничего не освободится.

Т.е. можно сформулировать проблему - какой синтаксис придумать, чтобы с одной стороны не было неявного выполнения кода, с другой стороны компилятор заставлял бы освобождать ресурс.

Можно, к примеру, такой подход использовать:

int f(int x) {
  int result;

  char *s = malloc(1);
  must_visit free_s;

  if (x == 0) {
    result = 0;
    goto free_s;
  }

  result = 1;

free_s:
  free(s);

  return result;
}

По сути этот код идентичен предыдущему. Всё на 100% явно, никакого неявного кода или неявных переходов. Но тут есть конструкция must_visit <label>. По задумке эта конструкция заставляет компилятор верифицировать, что все пути выполнения программы после этой конструкции достигают указанной метки. Если написать return 0, то компилятор увидит, что в этом случае метка free_s будет недостижима и выдаст ошибку компиляции.

Возможно этот подход был бы более практичным с точки зрения адаптации существующего кода.