Подтвердить что ты не робот

Нечеткость грамматики C11 между спецификатором _Atomic и спецификатором

Я пытаюсь написать грамматику lex/yacc для C11, основанной на N1570. Большая часть моей грамматики скопирована дословно из резюме информационного синтаксиса, но возникли некоторые конфликты yacc. Мне удалось решить все из них, кроме одного: кажется, что существует некоторая неопределенность между тем, когда "_Atomic" используется как спецификатор типа и когда он используется в качестве классификатора типов.

В форме спецификатора _Atomic сразу следует скобками, поэтому я предполагаю, что он имеет какое-то отношение к малоиспользуемому синтаксису C, который позволяет объявлять объявления в круглых скобках, тем самым позволяя скобкам сразу следовать квалификатору. Но моя грамматика уже знает, как отличать имена typedef от других идентификаторов, поэтому yacc должен знать разницу, не так ли?

Я не могу для жизни думать о случае, когда это было бы на самом деле двусмысленно.

Я сомневаюсь, что это помогает, но вот соответствующий выход состояния, который я получаю, когда я использую флаг yacc -v. "ATOMIC" - это, очевидно, мое имя для "_Atomic"

state 23

  152 atomic_type_specifier: ATOMIC . '(' type_name ')'
  156 type_qualifier: ATOMIC .

    '('  shift, and go to state 49

    '('       [reduce using rule 156 (type_qualifier)]
    $default  reduce using rule 156 (type_qualifier)
4b9b3361

Ответ 1

Да, я думаю, что в спецификации есть двусмысленность. Возьмем

_Atomic int (*f)(int);

здесь _Atomic является классификатором типа. (В качестве возвращаемого типа функции это не имеет большого смысла, но, действительно, я считаю). Теперь возьмите эту альтернативную форму

int _Atomic (*f)(int);

обычно тип-квалификаторы могут появляться после int, и это должно быть эквивалентно другому объявлению. Но теперь за _Atomic следует скобка, поэтому ее следует интерпретировать как спецификатор типа, который затем является синтаксической ошибкой. Я думаю, что даже можно было бы подготовить пример, где *f можно было бы заменить действительным typedef.

Взгляните на первую фразу 6.7.2.4 p4

Свойства, связанные с атомными типами, имеют смысл только для выражения, которые являются lvalues.

Это явно указывает, что они не ожидают, что типы возвращаемых функций будут _Atomic квалифицированными.

Edit:

Такая же двусмысленность возникла бы для

_Atomic int (*A)[3];

который имеет прекрасный смысл (указатель на массив из трех атомных целых чисел) и который мы должны переписать как

int _Atomic (*A)[3];

Изменить 2: Чтобы убедиться, что критерии наличия типа в скобках не устраняют неоднозначность, возьмите следующий допустимый код C99:

typedef int toto;

int main(void) {
  const int toto(void);
  int const toto(void);
  const int (toto)(void);
  int const (toto)(void);
  return toto();
}

Это переопределяет toto внутри main как функцию. И все четыре строки являются действительными прототипами для одной и той же функции. Теперь используйте _Atomic как квалификатор

typedef int toto;

int main(void) {
  int _Atomic (toto)(void);
  return toto();
}

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

Ответ 2

Хорошо, может ли мы придумать грамматически двусмысленный случай, не имеет значения. Раздел 6.7.2.4, пункт 4 N1570, гласит, что:

Если ключевому слову _Atomic сразу следует левая скобка, он интерпретируется как спецификатор типа (с именем типа), а не как спецификатор типа.

Чтобы обеспечить выполнение этого, я просто сделал _Atomic как спецификатор и _Atomic в качестве разделителя отдельных токенов в моих правилах lex.

"_Atomic"/{WHITESPACE}*"(" {return ATOMIC_SPECIFIER;}
"_Atomic"                  {return ATOMIC_QUALIFIER;}

Я относительно новичок в генераторах lex/yacc и parser в целом, но мой взгляд говорит, что это своего рода хак. В то же время, что еще может иметь синтаксис конечного контекста в lex для?