| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| // RUN: %clang_cc1 -std=c23 %s -fsyntax-only --embed-dir=%S/Inputs -verify | ||
| // expected-no-diagnostics | ||
|
|
||
| const char data[] = { | ||
| #embed <media/art.txt> | ||
| }; | ||
| const char data2[] = { | ||
| #embed <media/art.txt> | ||
| , 0 | ||
| }; | ||
| const char data3[] = { | ||
| #embed <media/art.txt> suffix(, 0) | ||
| }; | ||
| const char data4[] = { | ||
| #embed <media/art.txt> suffix(,) | ||
| 0 | ||
| }; | ||
| static_assert(sizeof(data) == 274); | ||
| static_assert(' ' == data[0]); | ||
| static_assert('_' == data[11]); | ||
| static_assert('\n' == data[273]); | ||
| static_assert(sizeof(data2) == 275); | ||
| static_assert(' ' == data2[0]); | ||
| static_assert('_' == data2[11]); | ||
| static_assert('\n' == data2[273]); | ||
| static_assert('\0' == data2[274]); | ||
| static_assert(sizeof(data3) == 275); | ||
| static_assert(' ' == data3[0]); | ||
| static_assert('_' == data3[11]); | ||
| static_assert('\n' == data3[273]); | ||
| static_assert('\0' == data3[274]); | ||
| static_assert(sizeof(data4) == 275); | ||
| static_assert(' ' == data4[0]); | ||
| static_assert('_' == data4[11]); | ||
| static_assert('\n' == data4[273]); | ||
| static_assert('\0' == data4[274]); | ||
|
|
||
| const signed char data5[] = { | ||
| #embed <media/art.txt> | ||
| }; | ||
| const signed char data6[] = { | ||
| #embed <media/art.txt> | ||
| , 0 | ||
| }; | ||
| const signed char data7[] = { | ||
| #embed <media/art.txt> suffix(, 0) | ||
| }; | ||
| const signed char data8[] = { | ||
| #embed <media/art.txt> suffix(,) | ||
| 0 | ||
| }; | ||
| static_assert(sizeof(data5) == 274); | ||
| static_assert(' ' == data5[0]); | ||
| static_assert('_' == data5[11]); | ||
| static_assert('\n' == data5[273]); | ||
| static_assert(sizeof(data6) == 275); | ||
| static_assert(' ' == data6[0]); | ||
| static_assert('_' == data6[11]); | ||
| static_assert('\n' == data6[273]); | ||
| static_assert('\0' == data6[274]); | ||
| static_assert(sizeof(data7) == 275); | ||
| static_assert(' ' == data7[0]); | ||
| static_assert('_' == data7[11]); | ||
| static_assert('\n' == data7[273]); | ||
| static_assert('\0' == data7[274]); | ||
| static_assert(sizeof(data8) == 275); | ||
| static_assert(' ' == data8[0]); | ||
| static_assert('_' == data8[11]); | ||
| static_assert('\n' == data8[273]); | ||
| static_assert('\0' == data8[274]); | ||
|
|
||
| const unsigned char data9[] = { | ||
| #embed <media/art.txt> | ||
| }; | ||
| const unsigned char data10[] = { | ||
| 0, | ||
| #embed <media/art.txt> | ||
| }; | ||
| const unsigned char data11[] = { | ||
| #embed <media/art.txt> prefix(0,) | ||
| }; | ||
| const unsigned char data12[] = { | ||
| 0 | ||
| #embed <media/art.txt> prefix(,) | ||
| }; | ||
| static_assert(sizeof(data9) == 274); | ||
| static_assert(' ' == data9[0]); | ||
| static_assert('_' == data9[11]); | ||
| static_assert('\n' == data9[273]); | ||
| static_assert(sizeof(data10) == 275); | ||
| static_assert(' ' == data10[1]); | ||
| static_assert('_' == data10[12]); | ||
| static_assert('\n' == data10[274]); | ||
| static_assert('\0' == data10[0]); | ||
| static_assert(sizeof(data11) == 275); | ||
| static_assert(' ' == data11[1]); | ||
| static_assert('_' == data11[12]); | ||
| static_assert('\n' == data11[274]); | ||
| static_assert('\0' == data11[0]); | ||
| static_assert(sizeof(data12) == 275); | ||
| static_assert(' ' == data12[1]); | ||
| static_assert('_' == data12[12]); | ||
| static_assert('\n' == data12[274]); | ||
| static_assert('\0' == data12[0]); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| // RUN: %clang_cc1 %s -triple x86_64 --embed-dir=%S/Inputs -emit-llvm -o - | FileCheck %s | ||
|
|
||
| // CHECK: @__const._Z3fooi.ca = private unnamed_addr constant [3 x i32] [i32 0, i32 106, i32 107], align 4 | ||
| // CHECK: @__const._Z3fooi.sc = private unnamed_addr constant %struct.S1 { i32 106, i32 107, i32 0 }, align 4 | ||
| // CHECK: @__const._Z3fooi.t = private unnamed_addr constant [3 x %struct.T] [%struct.T { [2 x i32] [i32 48, i32 49], %struct.S1 { i32 50, i32 51, i32 52 } }, %struct.T { [2 x i32] [i32 53, i32 54], %struct.S1 { i32 55, i32 56, i32 57 } }, %struct.T { [2 x i32] [i32 10, i32 0], %struct.S1 zeroinitializer }], align 16 | ||
| void foo(int a) { | ||
| // CHECK: %a.addr = alloca i32, align 4 | ||
| // CHECK: store i32 %a, ptr %a.addr, align 4 | ||
| // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %ca, ptr align 4 @__const._Z3fooi.ca, i64 12, i1 false) | ||
| int ca[] = { | ||
| 0 | ||
| #embed <jk.txt> prefix(,) | ||
| }; | ||
|
|
||
| // CHECK: %arrayinit.element = getelementptr inbounds i32, ptr %notca, i64 1 | ||
| // CHECK: store i8 106, ptr %arrayinit.element, align 4 | ||
| // CHECK: %arrayinit.element1 = getelementptr inbounds i32, ptr %notca, i64 2 | ||
| // CHECK: store i8 107, ptr %arrayinit.element1, align 4 | ||
| int notca[] = { | ||
| a | ||
| #embed <jk.txt> prefix(,) | ||
| }; | ||
|
|
||
| struct S1 { | ||
| int x, y, z; | ||
| }; | ||
|
|
||
| // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %sc, ptr align 4 @__const._Z3fooi.sc, i64 12, i1 false) | ||
| S1 sc = { | ||
| #embed <jk.txt> suffix(,) | ||
| 0 | ||
| }; | ||
|
|
||
| // CHECK: %x = getelementptr inbounds %struct.S1, ptr %s, i32 0, i32 0 | ||
| // CHECK: store i32 106, ptr %x, align 4 | ||
| // CHECK: %y = getelementptr inbounds %struct.S1, ptr %s, i32 0, i32 1 | ||
| // CHECK: store i32 107, ptr %y, align 4 | ||
| // CHECK: %z = getelementptr inbounds %struct.S1, ptr %s, i32 0, i32 2 | ||
| // CHECK: %1 = load i32, ptr %a.addr, align 4 | ||
| S1 s = { | ||
| #embed <jk.txt> suffix(,) | ||
| a | ||
| }; | ||
|
|
||
| // CHECK: store i32 107, ptr %b, align 4 | ||
| int b = | ||
| #embed<jk.txt> | ||
| ; | ||
|
|
||
|
|
||
| struct T { | ||
| int arr[2]; | ||
| struct S1 s; | ||
| }; | ||
|
|
||
| // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 16 %t, ptr align 16 @__const._Z3fooi.t, i64 60, i1 false) | ||
| constexpr struct T t[] = { | ||
| #embed <numbers.txt> | ||
| }; | ||
|
|
||
| // CHECK: %arr = getelementptr inbounds %struct.T, ptr %tnonc, i32 0, i32 0 | ||
| // CHECK: %2 = load i32, ptr %a.addr, align 4 | ||
| // CHECK: store i32 %2, ptr %arr, align 4 | ||
| // CHECK: %arrayinit.element2 = getelementptr inbounds i32, ptr %arr, i64 1 | ||
| // CHECK: store i32 300, ptr %arrayinit.element2, align 4 | ||
| // CHECK: %s3 = getelementptr inbounds %struct.T, ptr %tnonc, i32 0, i32 1 | ||
| // CHECK: %x4 = getelementptr inbounds %struct.S1, ptr %s3, i32 0, i32 0 | ||
| // CHECK: store i32 1, ptr %x4, align 4 | ||
| // CHECK: %y5 = getelementptr inbounds %struct.S1, ptr %s3, i32 0, i32 1 | ||
| // CHECK: store i32 2, ptr %y5, align 4 | ||
| // CHECK: %z6 = getelementptr inbounds %struct.S1, ptr %s3, i32 0, i32 2 | ||
| // CHECK: store i32 3, ptr %z6, align 4 | ||
| // CHECK: %arrayinit.element7 = getelementptr inbounds %struct.T, ptr %tnonc, i64 1 | ||
| // CHECK: call void @llvm.memset.p0.i64(ptr align 4 %arrayinit.element7, i8 0, i64 20, i1 false) | ||
| // CHECK: %arr8 = getelementptr inbounds %struct.T, ptr %arrayinit.element7, i32 0, i32 0 | ||
| // CHECK: store i8 106, ptr %arr8, align 4 | ||
| // CHECK: %arrayinit.element9 = getelementptr inbounds i32, ptr %arr8, i64 1 | ||
| // CHECK: store i8 107, ptr %arrayinit.element9, align 4 | ||
| struct T tnonc[] = { | ||
| a, 300, 1, 2, 3 | ||
| #embed <jk.txt> prefix(,) | ||
| }; | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| // RUN: %clang_cc1 %s -fsyntax-only --embed-dir=%S/Inputs -verify -Wno-c23-extensions | ||
| // RUN: %clang_cc1 %s -fsyntax-only --embed-dir=%S/Inputs -verify -fexperimental-new-constant-interpreter -Wno-c23-extensions | ||
|
|
||
| constexpr int value(int a, int b) { | ||
| return a + b; | ||
| } | ||
|
|
||
| constexpr int func_call() { | ||
| return value( | ||
| #embed <jk.txt> | ||
| ); | ||
| } | ||
|
|
||
| constexpr int init_list_expr() { | ||
| int vals[] = { | ||
| #embed <jk.txt> | ||
| }; | ||
| return value(vals[0], vals[1]); | ||
| } | ||
|
|
||
| template <int N, int M> | ||
| struct Hurr { | ||
| static constexpr int V1 = N; | ||
| static constexpr int V2 = M; | ||
| }; | ||
|
|
||
| constexpr int template_args() { | ||
| Hurr< | ||
| #embed <jk.txt> | ||
| > H; | ||
| return value(H.V1, H.V2); | ||
| } | ||
|
|
||
| constexpr int ExpectedValue = 'j' + 'k'; | ||
| static_assert(func_call() == ExpectedValue); | ||
| static_assert(init_list_expr() == ExpectedValue); | ||
| static_assert(template_args() == ExpectedValue); | ||
|
|
||
| static_assert( | ||
| #embed <jk.txt> limit(1) suffix(== 'j') | ||
| ); | ||
|
|
||
| int array[ | ||
| #embed <jk.txt> limit(1) | ||
| ]; | ||
| static_assert(sizeof(array) / sizeof(int) == 'j'); | ||
|
|
||
| constexpr int comma_expr = ( | ||
| #embed <jk.txt> // expected-warning {{left operand of comma operator has no effect}} | ||
| ); | ||
| static_assert(comma_expr == 'k'); | ||
|
|
||
| constexpr int comma_expr_init_list{ ( | ||
| #embed <jk.txt> limit(1) | ||
| ) }; | ||
| static_assert(comma_expr_init_list == 'j'); | ||
|
|
||
| constexpr int paren_init( | ||
| #embed <jk.txt> limit(1) | ||
| ); | ||
| static_assert(paren_init == 'j'); | ||
|
|
||
| struct S { | ||
| const char buffer[2] = { | ||
| #embed "jk.txt" | ||
| }; | ||
| }; | ||
|
|
||
| constexpr struct S s; | ||
| static_assert(s.buffer[1] == 'k'); | ||
|
|
||
| struct S1 { | ||
| int x, y; | ||
| }; | ||
|
|
||
| struct T { | ||
| int x, y; | ||
| struct S1 s; | ||
| }; | ||
|
|
||
| constexpr struct T t[] = { | ||
| #embed <numbers.txt> | ||
| }; | ||
| static_assert(t[0].s.x == '2'); | ||
|
|
||
| constexpr int func(int i, int) { return i; } | ||
| static_assert( | ||
| func( | ||
| #embed <jk.txt> | ||
| ) == 'j'); | ||
|
|
||
| template <int N> | ||
| struct ST {}; | ||
|
|
||
| ST< | ||
| #embed <jk.txt> limit(1) | ||
| > st; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| // RUN: %clang %s -fsyntax-only -std=c23 -M --embed-dir=%S/Inputs -Xclang -verify | FileCheck %s | ||
|
|
||
| // Yes this looks very strange indeed, but the goal is to test that we add | ||
| // files referenced by both __has_embed and #embed when we generate | ||
| // dependencies, so we're trying to see that both of these files are in the | ||
| // output. | ||
| #if __has_embed(<jk.txt>) | ||
| const char data = | ||
| #embed "Inputs/single_byte.txt" | ||
| ; | ||
| _Static_assert('b' == data); | ||
| #else | ||
| #error "oops" | ||
| #endif | ||
| // expected-no-diagnostics | ||
|
|
||
| // CHECK: embed_dependencies.c \ | ||
| // CHECK-NEXT: jk.txt \ | ||
| // CHECK-NEXT: Inputs{{[/\\]}}single_byte.txt | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| // RUN: %clang_cc1 -std=c23 %s -fsyntax-only --embed-dir=%S/Inputs -verify=none -pedantic | ||
| // RUN: %clang_cc1 -std=c23 %s -fsyntax-only --embed-dir=%S/Inputs -verify=compat -Wpre-c23-compat | ||
| // RUN: %clang_cc1 -std=c17 %s -fsyntax-only --embed-dir=%S/Inputs -verify=ext -pedantic | ||
| // RUN: %clang_cc1 -x c++ %s -fsyntax-only --embed-dir=%S/Inputs -verify=cxx -pedantic | ||
| // none-no-diagnostics | ||
|
|
||
| #if __has_embed("jk.txt") | ||
|
|
||
| const char buffer[] = { | ||
| #embed "jk.txt" /* compat-warning {{#embed is incompatible with C standards before C23}} | ||
| ext-warning {{#embed is a C23 extension}} | ||
| cxx-warning {{#embed is a Clang extension}} | ||
| */ | ||
| }; | ||
| #endif | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| // RUN: %clang_cc1 %s -E -CC -verify | ||
| // RUN: %clang_cc1 -x c %s -E -CC -verify | ||
| // expected-no-diagnostics | ||
|
|
||
| #if !defined(__has_embed) | ||
| #error 1 | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| // RUN: %clang_cc1 -std=c23 %s -E -verify | ||
|
|
||
| #embed <nfejfNejAKFe> | ||
| // expected-error@-1 {{'nfejfNejAKFe' file not found}} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| // RUN: %clang_cc1 -std=c23 %s -E -verify | ||
|
|
||
| #embed "nfejfNejAKFe" | ||
| // expected-error@-1 {{'nfejfNejAKFe' file not found}} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| // RUN: %clang_cc1 -std=c23 %s -fsyntax-only --embed-dir=%S/Inputs -verify | ||
| // RUN: %clang_cc1 -std=c23 %s -fsyntax-only --embed-dir=%S/Inputs -verify -fexperimental-new-constant-interpreter | ||
| // expected-no-diagnostics | ||
|
|
||
| typedef struct kitty { | ||
| int purr; | ||
| } kitty; | ||
|
|
||
| typedef struct kitty_kitty { | ||
| int here; | ||
| kitty kit; | ||
| } kitty_kitty; | ||
|
|
||
| const int meow = | ||
| #embed <single_byte.txt> | ||
| ; | ||
|
|
||
| const kitty kit = { | ||
| #embed <single_byte.txt> | ||
| }; | ||
|
|
||
| const kitty_kitty kit_kit = { | ||
| #embed <jk.txt> | ||
| }; | ||
|
|
||
| static_assert(meow == 'b'); | ||
| static_assert(kit.purr == 'b'); | ||
| static_assert(kit_kit.here == 'j'); | ||
| static_assert(kit_kit.kit.purr == 'k'); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| // RUN: %clang_cc1 -std=c23 %s --embed-dir=%S/Inputs -fsyntax-only -verify | ||
|
|
||
| const char data[] = { | ||
| #embed <media/empty> if_empty(123, 124, 125) | ||
| }; | ||
| const char non_empty_data[] = { | ||
| #embed <jk.txt> if_empty(123, 124, 125) | ||
| }; | ||
| static_assert(sizeof(data) == 3); | ||
| static_assert(123 == data[0]); | ||
| static_assert(124 == data[1]); | ||
| static_assert(125 == data[2]); | ||
| static_assert(sizeof(non_empty_data) == 2); | ||
| static_assert('j' == non_empty_data[0]); | ||
| static_assert('k' == non_empty_data[1]); | ||
|
|
||
| // Ensure we diagnose duplicate parameters even if they're the same value. | ||
| const unsigned char a[] = { | ||
| #embed <jk.txt> if_empty(1) prefix() if_empty(2) | ||
| // expected-error@-1 {{cannot specify parameter 'if_empty' twice in the same '#embed' directive}} | ||
| , | ||
| #embed <jk.txt> if_empty(1) suffix() if_empty(2) | ||
| // expected-error@-1 {{cannot specify parameter 'if_empty' twice in the same '#embed' directive}} | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| // RUN: %clang_cc1 -std=c23 %s --embed-dir=%S/Inputs -fsyntax-only -verify | ||
|
|
||
| const char data[] = { | ||
| #embed <jk.txt> | ||
| }; | ||
| const char offset_data[] = { | ||
| #embed <jk.txt> limit(1) | ||
| }; | ||
| static_assert(sizeof(data) == 2); | ||
| static_assert('j' == data[0]); | ||
| static_assert('k' == data[1]); | ||
| static_assert(sizeof(offset_data) == 1); | ||
| static_assert('j' == offset_data[0]); | ||
| static_assert(offset_data[0] == data[0]); | ||
|
|
||
| // Cannot have a negative limit. | ||
| #embed <jk.txt> limit(-1) | ||
| // expected-error@-1 {{invalid value '-1'; must be positive}} | ||
|
|
||
| // It can have a limit of 0, in which case the __has_embed should return false. | ||
| #if __has_embed(<jk.txt> limit(0)) != __STDC_EMBED_EMPTY__ | ||
| #error "__has_embed should return false when there's no data" | ||
| #endif | ||
|
|
||
| // When the limit is zero, the resource is empty, so if_empty kicks in. | ||
| const unsigned char buffer[] = { | ||
| #embed <jk.txt> limit(0) if_empty(1) | ||
| }; | ||
| static_assert(sizeof(buffer) == 1); | ||
| static_assert(buffer[0] == 1); | ||
|
|
||
| // However, prefix and suffix do not kick in. | ||
| const unsigned char other_buffer[] = { | ||
| 1, | ||
| #embed <jk.txt> limit(0) prefix(2,) suffix(3) | ||
| }; | ||
| static_assert(sizeof(other_buffer) == 1); | ||
| static_assert(other_buffer[0] == 1); | ||
|
|
||
| // Ensure we can limit to something larger than the file size as well. | ||
| const unsigned char third_buffer[] = { | ||
| #embed <jk.txt> limit(100) | ||
| }; | ||
| static_assert(sizeof(third_buffer) == 2); | ||
| static_assert('j' == third_buffer[0]); | ||
| static_assert('k' == third_buffer[1]); | ||
|
|
||
| // Test the limits of a file with more than one character in it. | ||
| const unsigned char fourth_buffer[] = { | ||
| #embed <media/art.txt> limit(10) | ||
| }; | ||
| static_assert(sizeof(fourth_buffer) == 10); | ||
| static_assert(' ' == fourth_buffer[0]); | ||
| static_assert(' ' == fourth_buffer[1]); | ||
| static_assert(' ' == fourth_buffer[2]); | ||
| static_assert(' ' == fourth_buffer[3]); | ||
| static_assert(' ' == fourth_buffer[4]); | ||
| static_assert(' ' == fourth_buffer[5]); | ||
| static_assert(' ' == fourth_buffer[6]); | ||
| static_assert(' ' == fourth_buffer[7]); | ||
| static_assert(' ' == fourth_buffer[8]); | ||
| static_assert(' ' == fourth_buffer[9]); | ||
|
|
||
| // Ensure that a limit larger than what can fit into a 64-bit value is | ||
| // rejected. This limit is fine because it fits in a 64-bit value. | ||
| const unsigned char fifth_buffer[] = { | ||
| #embed <jk.txt> limit(0xFFFF'FFFF'FFFF'FFFF) | ||
| }; | ||
| static_assert(sizeof(fifth_buffer) == 2); | ||
| static_assert('j' == fifth_buffer[0]); | ||
| static_assert('k' == fifth_buffer[1]); | ||
|
|
||
| // But this one is not fine because it does not fit into a 64-bit value. | ||
| const unsigned char sixth_buffer[] = { | ||
| #embed <jk.txt> limit(0xFFFF'FFFF'FFFF'FFFF'1) | ||
| }; | ||
| // expected-error@-2 {{integer literal is too large to be represented in any integer type}} | ||
| // Note: the preprocessor will continue with the truncated value, so the parser | ||
| // will treat this case and the previous one identically in terms of what | ||
| // contents are retained from the embedded resource (which is the entire file). | ||
|
|
||
| // Ensure we diagnose duplicate parameters even if they're the same value. | ||
| const unsigned char a[] = { | ||
| #embed <jk.txt> limit(1) prefix() limit(1) | ||
| // expected-error@-1 {{cannot specify parameter 'limit' twice in the same '#embed' directive}} | ||
| , | ||
| #embed <jk.txt> limit(1) if_empty() limit(2) | ||
| // expected-error@-1 {{cannot specify parameter 'limit' twice in the same '#embed' directive}} | ||
| }; | ||
|
|
||
| // C23 6.10.3.2p2 | ||
| static_assert( | ||
| #embed <jk.txt> limit(defined(FOO)) // expected-error {{'defined' cannot appear within this context}} | ||
| == 0); // expected-error {{expected expression}} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| // RUN: %clang_cc1 -std=c23 %s --embed-dir=%S/Inputs -fsyntax-only -verify | ||
|
|
||
| const char data[] = { | ||
| #embed <jk.txt> | ||
| }; | ||
| const char offset_data[] = { | ||
| #embed <jk.txt> clang::offset(1) | ||
| }; | ||
| static_assert(sizeof(data) == 2); | ||
| static_assert('j' == data[0]); | ||
| static_assert('k' == data[1]); | ||
| static_assert(sizeof(offset_data) == 1); | ||
| static_assert('k' == offset_data[0]); | ||
| static_assert(offset_data[0] == data[1]); | ||
|
|
||
| // Cannot have a negative offset. | ||
| #embed <jk.txt> clang::offset(-1) | ||
| // expected-error@-1 {{invalid value '-1'; must be positive}} | ||
|
|
||
| // If the offset is past the end of the file, the file should be considered | ||
| // empty. | ||
| #if __has_embed(<jk.txt> clang::offset(3)) != __STDC_EMBED_EMPTY__ | ||
| #error "__has_embed should return false when there's no data" | ||
| #endif | ||
|
|
||
| // When the offset is past the end of the file, the resource is empty, so if_empty kicks in. | ||
| const unsigned char buffer[] = { | ||
| #embed <jk.txt> clang::offset(3) if_empty(1) | ||
| }; | ||
| static_assert(sizeof(buffer) == 1); | ||
| static_assert(buffer[0] == 1); | ||
|
|
||
| // However, prefix and suffix do not kick in. | ||
| const unsigned char other_buffer[] = { | ||
| 1, | ||
| #embed <jk.txt> clang::offset(3) prefix(2,) suffix(3) | ||
| }; | ||
| static_assert(sizeof(other_buffer) == 1); | ||
| static_assert(other_buffer[0] == 1); | ||
|
|
||
| // Ensure we can offset to zero (that's the default behavior) | ||
| const unsigned char third_buffer[] = { | ||
| #embed <jk.txt> clang::offset(0) | ||
| }; | ||
| static_assert(sizeof(third_buffer) == 2); | ||
| static_assert('j' == third_buffer[0]); | ||
| static_assert('k' == third_buffer[1]); | ||
|
|
||
| // Test the offsets of a file with more than one character in it. | ||
| const unsigned char fourth_buffer[] = { | ||
| #embed <media/art.txt> clang::offset(24) limit(4) | ||
| }; | ||
| static_assert(sizeof(fourth_buffer) == 4); | ||
| static_assert('.' == fourth_buffer[0]); | ||
| static_assert('-' == fourth_buffer[1]); | ||
| static_assert('.' == fourth_buffer[2]); | ||
| static_assert('\'' == fourth_buffer[3]); | ||
|
|
||
| // Ensure that an offset larger than what can fit into a 64-bit value is | ||
| // rejected. This offset is fine because it fits in a 64-bit value. | ||
| const unsigned char fifth_buffer[] = { | ||
| 1, | ||
| #embed <jk.txt> clang::offset(0xFFFF'FFFF'FFFF'FFFF) | ||
| }; | ||
| static_assert(sizeof(fifth_buffer) == 1); | ||
| static_assert(1 == fifth_buffer[0]); | ||
|
|
||
| // But this one is not fine because it does not fit into a 64-bit value. | ||
| const unsigned char sixth_buffer[] = { | ||
| #embed <jk.txt> clang::offset(0xFFFF'FFFF'FFFF'FFFF'1) | ||
| }; | ||
| // expected-error@-2 {{integer literal is too large to be represented in any integer type}} | ||
|
|
||
| // Ensure we diagnose duplicate parameters even if they're the same value. | ||
| const unsigned char a[] = { | ||
| #embed <jk.txt> clang::offset(1) prefix() clang::offset(1) | ||
| // expected-error@-1 {{cannot specify parameter 'clang::offset' twice in the same '#embed' directive}} | ||
| , | ||
| #embed <jk.txt> clang::offset(1) if_empty() clang::offset(2) | ||
| // expected-error@-1 {{cannot specify parameter 'clang::offset' twice in the same '#embed' directive}} | ||
| }; | ||
|
|
||
| // Matches with C23 6.10.3.2p2, is documented as part of our extension. | ||
| static_assert( | ||
| #embed <jk.txt> clang::offset(defined(FOO)) | ||
| == 0); // expected-error {{expected expression}} | ||
| /* expected-error@-2 {{'defined' cannot appear within this context}} | ||
| pedantic-warning@-2 {{'clang::offset' is a Clang extension}} | ||
| */ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| // RUN: %clang_cc1 -std=c23 %s --embed-dir=%S/Inputs -fsyntax-only -verify | ||
|
|
||
| const char data[] = { | ||
| #embed <single_byte.txt> prefix('\xA', ) | ||
| }; | ||
| const char empty_data[] = { | ||
| #embed <media/empty> prefix('\xA', ) | ||
| 1 | ||
| }; | ||
| static_assert(sizeof(data) == 2); | ||
| static_assert('\xA' == data[0]); | ||
| static_assert('b' == data[1]); | ||
| static_assert(sizeof(empty_data) == 1); | ||
| static_assert(1 == empty_data[0]); | ||
|
|
||
| struct S { | ||
| int x, y, z; | ||
| }; | ||
|
|
||
| const struct S s = { | ||
| #embed <single_byte.txt> prefix( .x = 100, .y = 10, ) | ||
| }; | ||
| static_assert(s.x == 100); | ||
| static_assert(s.y == 10); | ||
| static_assert(s.z == 'b'); | ||
|
|
||
| // Ensure that an empty file does not produce any prefix tokens. If it did, | ||
| // there would be random tokens here that the parser would trip on. | ||
| #embed <media/empty> prefix(0) | ||
|
|
||
| // Ensure we diagnose duplicate parameters even if they're the same value. | ||
| const unsigned char a[] = { | ||
| #embed <jk.txt> prefix(1,) limit(1) prefix(1,) | ||
| // expected-error@-1 {{cannot specify parameter 'prefix' twice in the same '#embed' directive}} | ||
| , | ||
| #embed <jk.txt> prefix(1,) if_empty() prefix(2,) | ||
| // expected-error@-1 {{cannot specify parameter 'prefix' twice in the same '#embed' directive}} | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| // RUN: %clang_cc1 -std=c23 %s --embed-dir=%S/Inputs -fsyntax-only -verify | ||
|
|
||
| const char data[] = { | ||
| #embed <single_byte.txt> suffix(, '\xA') | ||
| }; | ||
| const char empty_data[] = { | ||
| #embed <media/empty> suffix(, '\xA') | ||
| 1 | ||
| }; | ||
| static_assert(sizeof(data) == 2); | ||
| static_assert('b' == data[0]); | ||
| static_assert('\xA' == data[1]); | ||
| static_assert(sizeof(empty_data) == 1); | ||
| static_assert(1 == empty_data[0]); | ||
|
|
||
| struct S { | ||
| int x, y, z; | ||
| }; | ||
|
|
||
| const struct S s = { | ||
| #embed <single_byte.txt> suffix( , .y = 100, .z = 10 ) | ||
| }; | ||
|
|
||
| static_assert(s.x == 'b'); | ||
| static_assert(s.y == 100); | ||
| static_assert(s.z == 10); | ||
|
|
||
| // Ensure that an empty file does not produce any suffix tokens. If it did, | ||
| // there would be random tokens here that the parser would trip on. | ||
| #embed <media/empty> suffix(0) | ||
|
|
||
| // Ensure we diagnose duplicate parameters even if they're the same value. | ||
| const unsigned char a[] = { | ||
| #embed <jk.txt> suffix(,1) prefix() suffix(,1) | ||
| // expected-error@-1 {{cannot specify parameter 'suffix' twice in the same '#embed' directive}} | ||
| , | ||
| #embed <jk.txt> suffix(,1) if_empty() suffix(,2) | ||
| // expected-error@-1 {{cannot specify parameter 'suffix' twice in the same '#embed' directive}} | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| // RUN: %clang_cc1 %s -std=c23 -E -verify | ||
| // okay-no-diagnostics | ||
|
|
||
| #embed __FILE__ unrecognized | ||
| // expected-error@-1 {{unknown embed preprocessor parameter 'unrecognized'}} | ||
| #embed __FILE__ unrecognized::param | ||
| // expected-error@-1 {{unknown embed preprocessor parameter 'unrecognized::param'}} | ||
| #embed __FILE__ unrecognized::param(with, args) | ||
| // expected-error@-1 {{unknown embed preprocessor parameter 'unrecognized::param'}} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| // RUN: %clang_cc1 -std=c23 %s -E -verify | ||
|
|
||
| // Test the parsing behavior for #embed and all of its parameters to ensure we | ||
| // recover from failures gracefully. | ||
| char buffer[] = { | ||
| #embed | ||
| // expected-error@-1 {{expected "FILENAME" or <FILENAME>}} | ||
|
|
||
| #embed < | ||
| // expected-error@-1 {{expected '>'}} \ | ||
| expected-note@-1 {{to match this '<'}} | ||
|
|
||
| #embed " | ||
| // expected-error@-1 {{expected "FILENAME" or <FILENAME>}} \ | ||
| expected-warning@-1 {{missing terminating '"' character}} | ||
|
|
||
| #embed file.txt | ||
| // expected-error@-1{{expected "FILENAME" or <FILENAME>}} | ||
|
|
||
| #embed "embed_parsing_errors.c" xxx | ||
| // expected-error@-1 {{unknown embed preprocessor parameter 'xxx'}} | ||
|
|
||
| #embed "embed_parsing_errors.c" xxx:: | ||
| // expected-error@-1 {{expected identifier}} | ||
|
|
||
| #embed "embed_parsing_errors.c" xxx::xxx | ||
| // expected-error@-1 {{unknown embed preprocessor parameter 'xxx::xxx'}} | ||
|
|
||
| #embed "embed_parsing_errors.c" xxx::42 | ||
| // expected-error@-1 {{expected identifier}} | ||
|
|
||
| #embed "embed_parsing_errors.c" limit | ||
| // expected-error@-1 {{expected '('}} | ||
|
|
||
| #embed "embed_parsing_errors.c" limit( | ||
| // expected-error@-1 {{expected value in expression}} | ||
|
|
||
| #embed "embed_parsing_errors.c" limit(xxx | ||
| // expected-error@-1 {{expected ')'}} | ||
|
|
||
| #embed "embed_parsing_errors.c" limit(42 | ||
| // expected-error@-1 {{expected ')'}} | ||
|
|
||
| #embed "embed_parsing_errors.c" limit([ | ||
| // expected-error@-1 {{invalid token at start of a preprocessor expression}} | ||
|
|
||
| #embed "embed_parsing_errors.c" limit([) | ||
| // expected-error@-1 {{invalid token at start of a preprocessor expression}} | ||
|
|
||
| #embed "embed_parsing_errors.c" limit(1/0) | ||
| // expected-error@-1 {{division by zero in preprocessor expression}} | ||
|
|
||
| #embed "embed_parsing_errors.c" clang::offset | ||
| // expected-error@-1 {{expected '('}} | ||
|
|
||
| #embed "embed_parsing_errors.c" clang::offset( | ||
| // expected-error@-1 {{expected value in expression}} | ||
|
|
||
| #embed "embed_parsing_errors.c" clang::offset(xxx | ||
| // expected-error@-1 {{expected ')'}} | ||
|
|
||
| #embed "embed_parsing_errors.c" clang::offset(42 | ||
| // expected-error@-1 {{expected ')'}} | ||
|
|
||
| #embed "embed_parsing_errors.c" clang::offset([ | ||
| // expected-error@-1 {{invalid token at start of a preprocessor expression}} | ||
|
|
||
| #embed "embed_parsing_errors.c" clang::offset([) | ||
| // expected-error@-1 {{invalid token at start of a preprocessor expression}} | ||
|
|
||
| #embed "embed_parsing_errors.c" clang::offset(1/0) | ||
| // expected-error@-1 {{division by zero in preprocessor expression}} | ||
|
|
||
| #embed "embed_parsing_errors.c" clang::offset 42 | ||
| // expected-error@-1 {{expected '('}} | ||
|
|
||
| #embed "embed_parsing_errors.c" prefix | ||
| // expected-error@-1 {{expected '('}} | ||
|
|
||
| #embed "embed_parsing_errors.c" prefix( | ||
| // expected-error@-1 {{expected ')'}} | ||
|
|
||
| #embed "embed_parsing_errors.c" prefix(xxx | ||
| // expected-error@-1 {{expected ')'}} | ||
|
|
||
| #embed "embed_parsing_errors.c" prefix(1/0) // OK: emitted as tokens, not evaluated yet. | ||
| #embed "embed_parsing_errors.c" prefix(([{}])) // OK: delimiters balanced | ||
| #embed "embed_parsing_errors.c" prefix(([{)]}) | ||
| // expected-error@-1 {{expected '}'}} expected-note@-1 {{to match this '{'}} | ||
| #embed "embed_parsing_errors.c" prefix(([{})}) | ||
| // expected-error@-1 {{expected ']'}} expected-note@-1 {{to match this '['}} | ||
| #embed "embed_parsing_errors.c" prefix(([{}]}) | ||
| // expected-error@-1 {{expected ')'}} expected-note@-1 {{to match this '('}} | ||
| #embed "embed_parsing_errors.c" prefix() // OK: tokens within parens are optional | ||
| #embed "embed_parsing_errors.c" prefix) | ||
| // expected-error@-1 {{expected '('}} | ||
|
|
||
| #embed "embed_parsing_errors.c" suffix | ||
| // expected-error@-1 {{expected '('}} | ||
|
|
||
| #embed "embed_parsing_errors.c" suffix( | ||
| // expected-error@-1 {{expected ')'}} | ||
|
|
||
| #embed "embed_parsing_errors.c" suffix(xxx | ||
| // expected-error@-1 {{expected ')'}} | ||
|
|
||
| #embed "embed_parsing_errors.c" suffix(1/0) // OK: emitted as tokens, not evaluated yet. | ||
| #embed "embed_parsing_errors.c" suffix(([{}])) // OK: delimiters balanced | ||
| #embed "embed_parsing_errors.c" suffix(([{)]}) | ||
| // expected-error@-1 {{expected '}'}} expected-note@-1 {{to match this '{'}} | ||
| #embed "embed_parsing_errors.c" suffix(([{})}) | ||
| // expected-error@-1 {{expected ']'}} expected-note@-1 {{to match this '['}} | ||
| #embed "embed_parsing_errors.c" suffix(([{}]}) | ||
| // expected-error@-1 {{expected ')'}} expected-note@-1 {{to match this '('}} | ||
| #embed "embed_parsing_errors.c" suffix() // OK: tokens within parens are optional | ||
| #embed "embed_parsing_errors.c" suffix) | ||
| // expected-error@-1 {{expected '('}} | ||
|
|
||
| #embed "embed_parsing_errors.c" if_empty(1/0) // OK: emitted as tokens, not evaluated yet. | ||
| #embed "embed_parsing_errors.c" if_empty(([{}])) // OK: delimiters balanced | ||
| #embed "embed_parsing_errors.c" if_empty(([{)]}) | ||
| // expected-error@-1 {{expected '}'}} expected-note@-1 {{to match this '{'}} | ||
| #embed "embed_parsing_errors.c" if_empty(([{})}) | ||
| // expected-error@-1 {{expected ']'}} expected-note@-1 {{to match this '['}} | ||
| #embed "embed_parsing_errors.c" if_empty(([{}]}) | ||
| // expected-error@-1 {{expected ')'}} expected-note@-1 {{to match this '('}} | ||
| #embed "embed_parsing_errors.c" if_empty() // OK: tokens within parens are optional | ||
| #embed "embed_parsing_errors.c" if_empty) | ||
| // expected-error@-1 {{expected '('}} | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| // RUN: %clang_cc1 %s -std=c23 -fsyntax-only --embed-dir=%S/Inputs -verify | ||
| // expected-no-diagnostics | ||
|
|
||
| const char data[] = { | ||
| #embed <single_byte.txt> | ||
| }; | ||
| static_assert(sizeof(data) == 1); | ||
| static_assert('b' == data[0]); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| // RUN: %clang_cc1 -std=c23 %s -fsyntax-only --embed-dir=%S/Inputs -verify | ||
| // expected-no-diagnostics | ||
|
|
||
| const char data[] = { | ||
| #embed "single_byte.txt" | ||
| }; | ||
| static_assert(sizeof(data) == 1); | ||
| static_assert('a' == data[0]); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| // RUN: %clang_cc1 -std=c23 %s -E --embed-dir=%S/Inputs | FileCheck %s --check-prefix EXPANDED | ||
| // RUN: %clang_cc1 -std=c23 %s -E -dE --embed-dir=%S/Inputs | FileCheck %s --check-prefix DIRECTIVE | ||
|
|
||
| // Ensure that we correctly preprocess to a file, both with expanding embed | ||
| // directives fully and with printing the directive instead. | ||
| const char data[] = { | ||
| #embed <jk.txt> if_empty('a', 'b') clang::offset(0) limit(1) suffix(, 'a', 0) prefix('h',) | ||
| }; | ||
|
|
||
| // EXPANDED: const char data[] = {'h',106 , 'a', 0}; | ||
| // DIRECTIVE: const char data[] = { | ||
| // DIRECTIVE-NEXT: #embed <jk.txt> if_empty('a', 'b') limit(1) clang::offset(0) prefix('h',) suffix(, 'a', 0) /* clang -E -dE */ | ||
| // DIRECTIVE-NEXT: }; | ||
|
|
||
| const char more[] = { | ||
| #embed <media/empty> if_empty('a', 'b') | ||
| }; | ||
|
|
||
| // EXPANDED: const char more[] = {'a', 'b'} | ||
| // DIRECTIVE: const char more[] = { | ||
| // DIRECTIVE-NEXT: #embed <media/empty> if_empty('a', 'b') /* clang -E -dE */ | ||
| // DIRECTIVE-NEXT: }; | ||
|
|
||
| const char even_more[] = { | ||
| 1, 2, 3, | ||
| #embed <jk.txt> prefix(4, 5,) suffix(, 6, 7) | ||
| , 8, 9, 10 | ||
| }; | ||
|
|
||
| // EXPANDED: const char even_more[] = { | ||
| // EXPANDED-NEXT: 1, 2, 3,4, 5,106, 107 , 6, 7 , 8, 9, 10 | ||
| // EXPANDED-EMPTY: | ||
| // EXPANDED-EMPTY: | ||
| // EXPANDED-NEXT: }; | ||
| // DIRECTIVE: const char even_more[] = { | ||
| // DIRECTIVE-NEXT: 1, 2, 3, | ||
| // DIRECTIVE-NEXT: #embed <jk.txt> prefix(4, 5,) suffix(, 6, 7) /* clang -E -dE */ | ||
| // DIRECTIVE-NEXT: , 8, 9, 10 | ||
| // DIRECTIVE-NEXT: }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| // RUN: %clang_cc1 %s -fsyntax-only -std=c23 --embed-dir=%S/Inputs -verify | ||
|
|
||
| const char data = | ||
| #embed <single_byte.txt> | ||
| ; | ||
| _Static_assert('b' == data); | ||
| // expected-no-diagnostics |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| // RUN: printf "\0" > %S/Inputs/null_byte.bin | ||
| // RUN: %clang_cc1 %s -fsyntax-only --embed-dir=%S/Inputs -verify=expected,cxx -Wno-c23-extensions | ||
| // RUN: %clang_cc1 -x c -std=c23 %s -fsyntax-only --embed-dir=%S/Inputs -verify=expected,c | ||
| // RUN: rm %S/Inputs/null_byte.bin | ||
| #embed <media/empty> | ||
| ; | ||
|
|
||
| void f (unsigned char x) { (void)x;} | ||
| void g () {} | ||
| void h (unsigned char x, int y) {(void)x; (void)y;} | ||
| int i () { | ||
| return | ||
| #embed <single_byte.txt> | ||
| ; | ||
| } | ||
|
|
||
| _Static_assert( | ||
| #embed <single_byte.txt> suffix(,) | ||
| "" | ||
| ); | ||
| _Static_assert( | ||
| #embed <single_byte.txt> | ||
| , "" | ||
| ); | ||
| _Static_assert(sizeof( | ||
| #embed <single_byte.txt> | ||
| ) == | ||
| sizeof(unsigned char) | ||
| , "" | ||
| ); | ||
| _Static_assert(sizeof | ||
| #embed <single_byte.txt> | ||
| , "" | ||
| ); | ||
| _Static_assert(sizeof( | ||
| #embed <jk.txt> // expected-warning {{left operand of comma operator has no effect}} | ||
| ) == | ||
| sizeof(unsigned char) | ||
| , "" | ||
| ); | ||
|
|
||
| #ifdef __cplusplus | ||
| template <int First, int Second> | ||
| void j() { | ||
| static_assert(First == 'j', ""); | ||
| static_assert(Second == 'k', ""); | ||
| } | ||
| #endif | ||
|
|
||
| void do_stuff() { | ||
| f( | ||
| #embed <single_byte.txt> | ||
| ); | ||
| g( | ||
| #embed <media/empty> | ||
| ); | ||
| h( | ||
| #embed <jk.txt> | ||
| ); | ||
| int r = i(); | ||
| (void)r; | ||
| #ifdef __cplusplus | ||
| j< | ||
| #embed <jk.txt> | ||
| >( | ||
| #embed <media/empty> | ||
| ); | ||
| #endif | ||
| } | ||
|
|
||
| // Ensure that we don't accidentally allow you to initialize an unsigned char * | ||
| // from embedded data; the data is modeled as a string literal internally, but | ||
| // is not actually a string literal. | ||
| const unsigned char *ptr = | ||
| #embed <jk.txt> // expected-warning {{left operand of comma operator has no effect}} | ||
| ; // c-error@-2 {{incompatible integer to pointer conversion initializing 'const unsigned char *' with an expression of type 'unsigned char'}} \ | ||
| cxx-error@-2 {{cannot initialize a variable of type 'const unsigned char *' with an rvalue of type 'unsigned char'}} | ||
|
|
||
| // However, there are some cases where this is fine and should work. | ||
| const unsigned char *null_ptr_1 = | ||
| #embed <media/empty> if_empty(0) | ||
| ; | ||
|
|
||
| const unsigned char *null_ptr_2 = | ||
| #embed <null_byte.bin> | ||
| ; | ||
|
|
||
| const unsigned char *null_ptr_3 = { | ||
| #embed <null_byte.bin> | ||
| }; | ||
|
|
||
| #define FILE_NAME <null_byte.bin> | ||
| #define LIMIT 1 | ||
| #define OFFSET 0 | ||
| #define EMPTY_SUFFIX suffix() | ||
|
|
||
| constexpr unsigned char ch = | ||
| #embed FILE_NAME limit(LIMIT) clang::offset(OFFSET) EMPTY_SUFFIX | ||
| ; | ||
| static_assert(ch == 0); | ||
|
|
||
| void foobar(float x, char y, char z); // cxx-note {{candidate function not viable: requires 3 arguments, but 1 was provided}} | ||
| // c-note@-1 {{declared here}} | ||
| void g1() { foobar((float) // cxx-error {{no matching function for call to 'foobar'}} | ||
| #embed "numbers.txt" limit(3) // expected-warning {{left operand of comma operator has no effect}} | ||
| ); // c-error {{too few arguments to function call, expected 3, have 1}} | ||
| } | ||
|
|
||
| #if __cplusplus | ||
| struct S { S(char x); ~S(); }; | ||
| void f1() { | ||
| S s[] = { | ||
| #embed "null_byte.bin" | ||
| }; | ||
| } | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| a |