mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-30 06:46:08 +00:00

OpenMP 5.1 added support for writing OpenMP directives using [[]] syntax in addition to using #pragma and this introduces support for the new syntax. In OpenMP, the attributes take one of two forms: [[omp::directive(...)]] or [[omp::sequence(...)]]. A directive attribute contains an OpenMP directive clause that is identical to the analogous #pragma syntax. A sequence attribute can contain either sequence or directive arguments and is used to ensure that the attributes are processed sequentially for situations where the order of the attributes matter (remember: https://eel.is/c++draft/dcl.attr.grammar#4.sentence-4). The approach taken here is somewhat novel and deserves mention. We could refactor much of the OpenMP parsing logic to work for either pragma annotation tokens or for attribute clauses. It would be a fair amount of effort to share the logic for both, but it's certainly doable. However, the semantic attribute system is not designed to handle the arbitrarily complex arguments that OpenMP directives contain. Adding support to thread the novel parsed information until we can produce a semantic attribute would be considerably more effort. What's more, existing OpenMP constructs are not (often) represented as semantic attributes. So doing this through Attr.td would be a massive undertaking that would likely only benefit OpenMP and comes with additional risks. Rather than walk down that path, I am taking advantage of the fact that the syntax of the directives within the directive clause is identical to that of the #pragma form. Once the parser recognizes that we're processing an OpenMP attribute, it caches all of the directive argument tokens and then replays them as though the user wrote a pragma. This reuses the same OpenMP parsing and semantic logic directly, but does come with a risk if the OpenMP committee decides to purposefully diverge their pragma and attribute syntaxes. So, despite this being a novel approach that does token replay, I think it's actually a better approach than trying to do this through the declarative syntax in Attr.td.
78 lines
5.1 KiB
C++
78 lines
5.1 KiB
C++
// RUN: %clang_cc1 -std=c++17 -fopenmp -fopenmp-version=51 -fsyntax-only -verify %s
|
|
|
|
// This file tests the custom parsing logic for the OpenMP 5.1 attribute
|
|
// syntax. It does not test actual OpenMP directive syntax, just the attribute
|
|
// parsing bits.
|
|
|
|
// FIXME: the diagnostic here is a bit unsatisfying. We handle the custom omp
|
|
// attribute parsing logic when parsing the attribute argument list, and we
|
|
// only process an attribute argument list when we see an open paren after the
|
|
// attribute name. So this means we never hit the omp-specific parsing and
|
|
// instead handle this through the usual Sema attribute handling in
|
|
// SemaDeclAttr.cpp, which diagnoses this as an unknown attribute.
|
|
[[omp::directive]]; // expected-warning {{unknown attribute 'directive' ignored}}
|
|
[[omp::sequence]]; // expected-warning {{unknown attribute 'sequence' ignored}}
|
|
[[omp::unknown]]; // expected-warning {{unknown attribute 'unknown' ignored}}
|
|
|
|
[[omp::directive()]]; // expected-error {{expected an OpenMP directive}}
|
|
[[omp::sequence()]]; // expected-error {{expected an OpenMP 'directive' or 'sequence' attribute argument}}
|
|
|
|
// Both sequence and directive require an argument list, test that we diagnose
|
|
// when the inner directive or sequence is missing its argument list.
|
|
[[omp::sequence(directive)]]; // expected-error {{expected '('}}
|
|
[[omp::sequence(sequence)]]; // expected-error {{expected '('}}
|
|
[[omp::sequence(omp::directive)]]; // expected-error {{expected '('}}
|
|
[[omp::sequence(omp::sequence)]]; // expected-error {{expected '('}}
|
|
|
|
// All of the diagnostics here come from the inner sequence and directive not
|
|
// being given an argument, but this tests that we can parse either with or
|
|
// without the 'omp::'.
|
|
[[omp::sequence(directive(), sequence())]]; // expected-error {{expected an OpenMP directive}} expected-error {{expected an OpenMP 'directive' or 'sequence' attribute argument}}
|
|
[[omp::sequence(omp::directive(), sequence())]]; // expected-error {{expected an OpenMP directive}} expected-error {{expected an OpenMP 'directive' or 'sequence' attribute argument}}
|
|
[[omp::sequence(directive(), omp::sequence())]]; // expected-error {{expected an OpenMP directive}} expected-error {{expected an OpenMP 'directive' or 'sequence' attribute argument}}
|
|
[[omp::sequence(omp::directive(), omp::sequence())]]; // expected-error {{expected an OpenMP directive}} expected-error {{expected an OpenMP 'directive' or 'sequence' attribute argument}}
|
|
|
|
// Test that we properly diagnose missing parens within the inner arguments of
|
|
// a sequence attribute.
|
|
[[omp::sequence( // expected-note {{to match this '('}}
|
|
directive(
|
|
)]]; // expected-error {{expected ')'}} expected-error {{expected an OpenMP directive}}
|
|
[[omp::sequence( // expected-note {{to match this '('}}
|
|
sequence(
|
|
)]]; // expected-error {{expected ')'}} expected-error {{expected an OpenMP 'directive' or 'sequence' attribute argument}}
|
|
|
|
// Test that we properly handle the using attribute syntax.
|
|
[[using omp: directive()]]; // expected-error {{expected an OpenMP directive}}
|
|
[[using omp: sequence()]]; // expected-error {{expected an OpenMP 'directive' or 'sequence' attribute argument}}
|
|
[[using omp: sequence(omp::directive())]]; // expected-error {{expected an OpenMP directive}}
|
|
[[using omp: sequence(directive())]]; // expected-error {{expected an OpenMP directive}}
|
|
|
|
// Test that we give a sensible error on an unknown attribute in the omp
|
|
// namespace that has an argument list.
|
|
[[omp::unknown()]]; // expected-warning {{unknown attribute 'unknown' ignored}}
|
|
[[using omp: unknown()]]; // expected-warning {{unknown attribute 'unknown' ignored}}
|
|
|
|
// Test that unknown arguments to the omp::sequence are rejected, regardless of
|
|
// what level they're at.
|
|
[[omp::sequence(unknown)]]; // expected-error {{expected an OpenMP 'directive' or 'sequence' attribute argument}}
|
|
[[omp::sequence(sequence(unknown))]]; // expected-error {{expected an OpenMP 'directive' or 'sequence' attribute argument}}
|
|
[[omp::sequence(omp::unknown)]]; // expected-error {{expected an OpenMP 'directive' or 'sequence' attribute argument}}
|
|
[[omp::sequence(sequence(omp::unknown))]]; // expected-error {{expected an OpenMP 'directive' or 'sequence' attribute argument}}
|
|
|
|
// FIXME: combining non-openmp attributes with openmp attributes has surprising
|
|
// results due to the replay of tokens. We properly parse the non-openmp
|
|
// attributes, but we also replay the OpenMP tokens. The attributes then get
|
|
// passed to the OpenMP parsing functions and it does not attach the attribute
|
|
// to the declaration statement AST node as you might expect. This means that
|
|
// the expected diagnostics are not issued. Thankfully, due to the positioning
|
|
// of OpenMP attributes and what they appertain to, this should not be a
|
|
// frequent issue (hopefully).
|
|
int x;
|
|
[[deprecated, omp::directive(threadprivate(x))]] int y; // FIXME-expected-note {{'y' has been explicitly marked deprecated here}}
|
|
[[omp::directive(threadprivate(x)), deprecated]] int z; // FIXME-expected-note {{'z' has been explicitly marked deprecated here}}
|
|
void test() {
|
|
x = 1;
|
|
y = 1; // FIXME-expected-warning {{warning: 'y' is deprecated}}
|
|
z = 1; // FIXME-expected-warning {{warning: 'z' is deprecated}}
|
|
}
|