Skip to content

Commit

Permalink
Fix handling of empty vararg pasting
Browse files Browse the repository at this point in the history
(GCC extension)
  • Loading branch information
dspinellis committed Jul 4, 2024
1 parent 8f79988 commit acbf004
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 15 deletions.
30 changes: 15 additions & 15 deletions src/macro.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -518,24 +518,24 @@ subst(const Macro &m, dequePtoken is, const mapArgval &args, HideSet hs, bool sk
if (ti != is.end() && ti->get_code() == CPP_CONCAT) {
/*
* Implement the following gcc extension:
* "`##' before a
* rest argument that is empty discards the preceding sequence of
* non-whitespace characters from the macro definition. (If another macro
* argument precedes, none of it is discarded.)"
* Otherwise, break to process a non-formal argument in the default way
* "`##' before a rest argument that is empty
* discards the preceding "," token from
* the macro definition.
* Otherwise, break to process a non-formal
* argument in the default way
*/
if ((ai = find_formal_argument(args, head)) == args.end()) {
if (m.get_is_vararg()) {
ti2 = find_nonspace(ti + 1, is.end());
if (ti2 != is.end() && (ai = find_formal_argument(args, *ti2)) != args.end() && ai->second.size() == 0) {
// All conditions satisfied; discard elements:
// <non-formal> <##> <empty-formal>
is.erase(is.begin(), ++ti2);
continue;
}
if (head.get_code() == ','
&& m.get_is_vararg()) {
ti2 = find_nonspace(ti + 1, is.end());
if (ti2 != is.end() && (ai = find_formal_argument(args, *ti2)) != args.end() && ai->second.size() == 0) {
// All conditions satisfied; discard elements:
// <non-formal> <##> <empty-formal>
is.erase(is.begin(), ++ti2);
continue;
}
break; // Non-formal arguments don't deserve special treatment
}
if ((ai = find_formal_argument(args, head)) == args.end())
break; // Non-formal arguments don't deserve special treatment
// Paste but not expand LHS, RHS
if (ai->second.size() == 0) { // Only if actuals can be empty
is.erase(is.begin(), ++ti); // Erase including ##
Expand Down
22 changes: 22 additions & 0 deletions src/test/cpp/cpp74-vararg-concat.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#define recip(x) (1 / x)
#define recip_safe(x) (x ? 1 / x : 0)

#define any_recip(x, ...) recip ## __VA_ARGS__(x)

#define error(x, ...) fprintf(stderr, x, ## __VA_ARGS__)

int
a()
{
// Test non-deletion of recip
int b = any_recip(2);

// Test addition of _safe
int c = any_recip(3, _safe);

// Test deletion of comma
error("Internal error");

// Test non-deletion of comma
error("Errno: %d", errno);
}
Empty file.
10 changes: 10 additions & 0 deletions src/test/out/cpp74-vararg-concat.c.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
int main();
static void _cscout_dummy1(void) { _cscout_dummy1(); }
int
a()
{
int b = (1 / 2);
int c = (3 ? 1 / 3 : 0);
fprintf(stderr, "Internal error");
fprintf(stderr, "Errno: %d",errno);
}

0 comments on commit acbf004

Please sign in to comment.