Skip to content

Commit

Permalink
C++: Support C11 _Generic expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
jketema committed Aug 3, 2024
1 parent f18c255 commit 5c964e5
Show file tree
Hide file tree
Showing 13 changed files with 667 additions and 6 deletions.
13 changes: 13 additions & 0 deletions cpp/ql/lib/semmle/code/cpp/PrintAST.qll
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,19 @@ class CastNode extends ConversionNode {
}
}

/**
* A node representing a `C11GenericExpr`.
*/
class C11GenericNode extends ConversionNode {
C11GenericExpr generic;

C11GenericNode() { generic = conv }

override AstNode getChildInternal(int childIndex) {
result.getAst() = generic.getChild(childIndex)
}
}

/**
* A node representing a `StmtExpr`.
*/
Expand Down
9 changes: 9 additions & 0 deletions cpp/ql/lib/semmle/code/cpp/exprs/Cast.qll
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,15 @@ class TemporaryObjectExpr extends Conversion, @temp_init {
override string getAPrimaryQlClass() { result = "TemporaryObjectExpr" }
}

/**
* A node representing a C11 `_Generic` expression.
*/
class C11GenericExpr extends Conversion, @c11_generic {
override string toString() { result = "_Generic" }

override string getAPrimaryQlClass() { result = "C11GenericExpr" }
}

/**
* A node representing the Cast sub-class of entity `cast`.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ private predicate ignoreExprAndDescendants(Expr expr) {
vaStartExpr.getLastNamedParameter().getFullyConverted() = expr
)
or
// The children of C11 _Generic expressions are just surface syntax.
exists(C11GenericExpr generic | generic.getAChild() = expr)
or
// Do not translate implicit destructor calls for unnamed temporary variables that are
// conditionally constructed (until we have a mechanism for calling these only when the
// temporary's constructor was run)
Expand Down Expand Up @@ -432,6 +435,9 @@ predicate ignoreLoad(Expr expr) {
// The load is duplicated from the right operand.
isExtractorFrontendVersion65OrHigher() and expr instanceof CommaExpr
or
// The load is duplicated from the chosen expression.
expr instanceof C11GenericExpr
or
expr.(PointerDereferenceExpr).getOperand().getFullyConverted().getType().getUnspecifiedType()
instanceof FunctionPointerType
or
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -893,7 +893,8 @@ class TranslatedTransparentConversion extends TranslatedTransparentExpr {
(
expr instanceof ParenthesisExpr or
expr instanceof ReferenceDereferenceExpr or
expr instanceof ReferenceToExpr
expr instanceof ReferenceToExpr or
expr instanceof C11GenericExpr
)
}

Expand Down
2 changes: 2 additions & 0 deletions cpp/ql/lib/semmlecode.cpp.dbscheme
Original file line number Diff line number Diff line change
Expand Up @@ -1210,6 +1210,7 @@ conversionkinds(
| @reference_to
| @ref_indirect
| @temp_init
| @c11_generic
;

/*
Expand Down Expand Up @@ -1788,6 +1789,7 @@ case @expr.kind of
| 382 = @isvalidwinrttype
| 383 = @iswinclass
| 384 = @iswininterface
| 385 = @c11_generic
;

@var_args_expr = @vastartexpr
Expand Down
450 changes: 450 additions & 0 deletions cpp/ql/test/library-tests/c11_generic/PrintAST.expected

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions cpp/ql/test/library-tests/c11_generic/PrintAST.qlref
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
semmle/code/cpp/PrintAST.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
| generic.c:21:22:21:32 | _Generic | generic.c:21:22:21:32 | describe(val) |
| generic.c:22:22:22:32 | _Generic | generic.c:22:22:22:32 | describe(val) |
| generic.c:23:22:23:32 | _Generic | generic.c:23:22:23:32 | describe(val) |
| generic.c:24:22:24:32 | _Generic | generic.c:24:22:24:32 | describe(val) |
| generic.cpp:22:22:22:32 | _Generic | generic.cpp:22:22:22:32 | describe(val) |
| generic.cpp:23:22:23:32 | _Generic | generic.cpp:23:22:23:32 | describe(val) |
| generic.cpp:24:22:24:32 | _Generic | generic.cpp:24:22:24:32 | describe(val) |
| generic.cpp:25:22:25:32 | _Generic | generic.cpp:25:22:25:32 | describe(val) |
5 changes: 5 additions & 0 deletions cpp/ql/test/library-tests/c11_generic/macro_invocation.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import cpp

from C11GenericExpr g, MacroInvocation m
where m.getAnExpandedElement() = g
select g, m
100 changes: 99 additions & 1 deletion cpp/ql/test/library-tests/ir/ir/PrintAST.expected
Original file line number Diff line number Diff line change
Expand Up @@ -4180,7 +4180,7 @@ destructors_for_temps.cpp:
# 103| ValueCategory = prvalue
# 104| getStmt(1): [ReturnStmt] return ...
generic.c:
# 7| [TopLevelFunction] char const* c11_generic()
# 7| [TopLevelFunction] char const* c11_generic_1()
# 7| <params>:
# 8| getEntryPoint(): [BlockStmt] { ... }
# 9| getStmt(0): [DeclStmt] declaration
Expand All @@ -4198,6 +4198,104 @@ generic.c:
# 11| getExpr(): [ArrayToPointerConversion] array to pointer conversion
# 11| Type = [CharPointerType] char *
# 11| ValueCategory = prvalue
# 11| getExpr(): [C11GenericExpr] _Generic
# 11| Type = [ArrayType] char[4]
# 11| ValueCategory = lvalue
# 11| getChild(0): [VariableAccess] i
# 11| Type = [IntType] int
# 11| ValueCategory = prvalue(load)
# 11| getChild(1): [TypeName] int
# 11| Type = [IntType] int
# 11| ValueCategory = prvalue
# 11| getChild(2): [ReuseExpr] reuse of int
# 11| Type = [ArrayType] char[4]
# 11| ValueCategory = lvalue
# 11| getChild(3): [TypeName] void
# 11| Type = [VoidType] void
# 11| ValueCategory = prvalue
# 11| getChild(4): unknown
# 11| Type = [ArrayType] char[8]
# 11| Value = [StringLiteral] "unknown"
# 11| ValueCategory = lvalue
# 11| getChild(0).getFullyConverted(): [ParenthesisExpr] (...)
# 11| Type = [IntType] int
# 11| ValueCategory = prvalue(load)
# 14| [TopLevelFunction] void enk_c11_generic_2(unsigned int, int)
# 14| <params>:
# 14| getParameter(0): [Parameter] x
# 14| Type = [IntType] unsigned int
# 14| getParameter(1): [Parameter] y
# 14| Type = [IntType] int
# 14| getEntryPoint(): [BlockStmt] { ... }
# 15| getStmt(0): [DeclStmt] declaration
# 15| getDeclarationEntry(0): [VariableDeclarationEntry] definition of r
# 15| Type = [IntType] unsigned int
# 16| getStmt(1): [ExprStmt] ExprStmt
# 16| getExpr(): [AssignExpr] ... = ...
# 16| Type = [IntType] unsigned int
# 16| ValueCategory = prvalue
# 16| getLValue(): [VariableAccess] r
# 16| Type = [IntType] unsigned int
# 16| ValueCategory = lvalue
# 16| getRValue(): [AddExpr] ... + ...
# 16| Type = [IntType] unsigned int
# 16| ValueCategory = prvalue
# 16| getLeftOperand(): [VariableAccess] x
# 16| Type = [IntType] unsigned int
# 16| ValueCategory = lvalue
# 16| getRightOperand(): [Literal] 1
# 16| Type = [IntType] int
# 16| Value = [Literal] 1
# 16| ValueCategory = prvalue
# 16| getLeftOperand().getFullyConverted(): [C11GenericExpr] _Generic
# 16| Type = [IntType] unsigned int
# 16| ValueCategory = prvalue
# 16| getChild(0): [VariableAccess] r
# 16| Type = [IntType] unsigned int
# 16| ValueCategory = prvalue(load)
# 16| getChild(1): [TypeName] unsigned int
# 16| Type = [IntType] unsigned int
# 16| ValueCategory = prvalue
# 16| getChild(2): [ReuseExpr] reuse of x
# 16| Type = [IntType] unsigned int
# 16| ValueCategory = lvalue
# 16| getChild(3): [TypeName] int
# 16| Type = [IntType] int
# 16| ValueCategory = prvalue
# 16| getChild(4): [VariableAccess] y
# 16| Type = [IntType] int
# 16| ValueCategory = lvalue
# 16| getRightOperand().getFullyConverted(): [CStyleCast] (unsigned int)...
# 16| Conversion = [IntegralConversion] integral conversion
# 16| Type = [IntType] unsigned int
# 16| Value = [CStyleCast] 1
# 16| ValueCategory = prvalue
# 18| getStmt(2): [DeclStmt] declaration
# 18| getDeclarationEntry(0): [VariableDeclarationEntry] definition of s
# 18| Type = [IntType] unsigned int
# 19| getStmt(3): [ExprStmt] ExprStmt
# 19| getExpr(): [AssignExpr] ... = ...
# 19| Type = [IntType] unsigned int
# 19| ValueCategory = prvalue
# 19| getLValue(): [VariableAccess] s
# 19| Type = [IntType] unsigned int
# 19| ValueCategory = lvalue
# 19| getRValue(): [AddExpr] ... + ...
# 19| Type = [IntType] unsigned int
# 19| ValueCategory = prvalue
# 19| getLeftOperand(): [VariableAccess] x
# 19| Type = [IntType] unsigned int
# 19| ValueCategory = prvalue(load)
# 19| getRightOperand(): [Literal] 1
# 19| Type = [IntType] int
# 19| Value = [Literal] 1
# 19| ValueCategory = prvalue
# 19| getRightOperand().getFullyConverted(): [CStyleCast] (unsigned int)...
# 19| Conversion = [IntegralConversion] integral conversion
# 19| Type = [IntType] unsigned int
# 19| Value = [CStyleCast] 1
# 19| ValueCategory = prvalue
# 20| getStmt(4): [ReturnStmt] return ...
ir.c:
# 5| [TopLevelFunction] int getX(MyCoords*)
# 5| <params>:
Expand Down
33 changes: 32 additions & 1 deletion cpp/ql/test/library-tests/ir/ir/aliased_ir.expected
Original file line number Diff line number Diff line change
Expand Up @@ -2959,7 +2959,7 @@ destructors_for_temps.cpp:
# 102| v102_10(void) = ExitFunction :

generic.c:
# 7| char const* c11_generic()
# 7| char const* c11_generic_1()
# 7| Block 0
# 7| v7_1(void) = EnterFunction :
# 7| m7_2(unknown) = AliasedDefinition :
Expand All @@ -2977,6 +2977,37 @@ generic.c:
# 7| v7_7(void) = AliasedUse : m7_3
# 7| v7_8(void) = ExitFunction :

# 14| void enk_c11_generic_2(unsigned int, int)
# 14| Block 0
# 14| v14_1(void) = EnterFunction :
# 14| m14_2(unknown) = AliasedDefinition :
# 14| m14_3(unknown) = InitializeNonLocal :
# 14| m14_4(unknown) = Chi : total:m14_2, partial:m14_3
# 14| r14_5(glval<unsigned int>) = VariableAddress[x] :
# 14| m14_6(unsigned int) = InitializeParameter[x] : &:r14_5
# 14| m14_7(unknown) = Chi : total:m14_4, partial:m14_6
# 14| r14_8(glval<int>) = VariableAddress[y] :
# 14| m14_9(int) = InitializeParameter[y] : &:r14_8
# 15| r15_1(glval<unsigned int>) = VariableAddress[r] :
# 15| m15_2(unsigned int) = Uninitialized[r] : &:r15_1
# 16| r16_1(glval<unsigned int>) = VariableAddress[x] :
# 16| r16_2(unsigned int) = Constant[1] :
# 16| r16_3(unsigned int) = Add : r16_1, r16_2
# 16| r16_4(glval<unsigned int>) = VariableAddress[r] :
# 16| m16_5(unsigned int) = Store[r] : &:r16_4, r16_3
# 18| r18_1(glval<unsigned int>) = VariableAddress[s] :
# 18| m18_2(unsigned int) = Uninitialized[s] : &:r18_1
# 19| r19_1(glval<unsigned int>) = VariableAddress[x] :
# 19| r19_2(unsigned int) = Load[x] : &:r19_1, m14_6
# 19| r19_3(unsigned int) = Constant[1] :
# 19| r19_4(unsigned int) = Add : r19_2, r19_3
# 19| r19_5(glval<unsigned int>) = VariableAddress[s] :
# 19| m19_6(unsigned int) = Store[s] : &:r19_5, r19_4
# 20| v20_1(void) = NoOp :
# 14| v14_10(void) = ReturnVoid :
# 14| v14_11(void) = AliasedUse : m14_3
# 14| v14_12(void) = ExitFunction :

ir.c:
# 7| void MyCoordsTest(int)
# 7| Block 0
Expand Down
12 changes: 10 additions & 2 deletions cpp/ql/test/library-tests/ir/ir/generic.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,19 @@
default: "unknown" \
)

const char *c11_generic()
const char *c11_generic_1()
{
int i;

return describe(i);
}

// semmle-extractor-options: -std=c11
void enk_c11_generic_2(unsigned int x, int y) {
unsigned int r;
r = _Generic(r, unsigned int: x, int: y) + 1;

unsigned int s;
s = x + 1;
}

// semmle-extractor-options: -std=c11
31 changes: 30 additions & 1 deletion cpp/ql/test/library-tests/ir/ir/raw_ir.expected
Original file line number Diff line number Diff line change
Expand Up @@ -2733,7 +2733,7 @@ destructors_for_temps.cpp:
# 102| v102_8(void) = ExitFunction :

generic.c:
# 7| char const* c11_generic()
# 7| char const* c11_generic_1()
# 7| Block 0
# 7| v7_1(void) = EnterFunction :
# 7| mu7_2(unknown) = AliasedDefinition :
Expand All @@ -2750,6 +2750,35 @@ generic.c:
# 7| v7_6(void) = AliasedUse : ~m?
# 7| v7_7(void) = ExitFunction :

# 14| void enk_c11_generic_2(unsigned int, int)
# 14| Block 0
# 14| v14_1(void) = EnterFunction :
# 14| mu14_2(unknown) = AliasedDefinition :
# 14| mu14_3(unknown) = InitializeNonLocal :
# 14| r14_4(glval<unsigned int>) = VariableAddress[x] :
# 14| mu14_5(unsigned int) = InitializeParameter[x] : &:r14_4
# 14| r14_6(glval<int>) = VariableAddress[y] :
# 14| mu14_7(int) = InitializeParameter[y] : &:r14_6
# 15| r15_1(glval<unsigned int>) = VariableAddress[r] :
# 15| mu15_2(unsigned int) = Uninitialized[r] : &:r15_1
# 16| r16_1(glval<unsigned int>) = VariableAddress[x] :
# 16| r16_2(unsigned int) = Constant[1] :
# 16| r16_3(unsigned int) = Add : r16_1, r16_2
# 16| r16_4(glval<unsigned int>) = VariableAddress[r] :
# 16| mu16_5(unsigned int) = Store[r] : &:r16_4, r16_3
# 18| r18_1(glval<unsigned int>) = VariableAddress[s] :
# 18| mu18_2(unsigned int) = Uninitialized[s] : &:r18_1
# 19| r19_1(glval<unsigned int>) = VariableAddress[x] :
# 19| r19_2(unsigned int) = Load[x] : &:r19_1, ~m?
# 19| r19_3(unsigned int) = Constant[1] :
# 19| r19_4(unsigned int) = Add : r19_2, r19_3
# 19| r19_5(glval<unsigned int>) = VariableAddress[s] :
# 19| mu19_6(unsigned int) = Store[s] : &:r19_5, r19_4
# 20| v20_1(void) = NoOp :
# 14| v14_8(void) = ReturnVoid :
# 14| v14_9(void) = AliasedUse : ~m?
# 14| v14_10(void) = ExitFunction :

ir.c:
# 7| void MyCoordsTest(int)
# 7| Block 0
Expand Down

0 comments on commit 5c964e5

Please sign in to comment.