# Language compatibility The LLVM compiler strives to both conform to current language standards, and to implement many widely used extensions available in other compilers. Thus, most correct code will *just work* when compiled with this compiler. However, the LLVM compiler is more strict than other popular compilers, and it might reject incorrect code that other compilers allow. This section describes common compatibility and portability issues with the LLVM compiler to help you understand and fix the problem in your code when this compiler emits an error message. ## C compatibility This section describes common compatibility and portability issues with LLVM C. ### Differences between various standard modes The LLVM compiler supports the `-std` option, which changes what language mode LLVM uses. The supported modes for C are c89, gnu89, c94, c99, gnu99, c11, gnu11, and various aliases for those modes. If no `-std` option is specified, the LLVM compiler defaults to gnu99 mode. The c\* and gnu\* modes have the following differences: - The c\* modes define `__STRICT_ANSI__`. - Target-specific defines not prefixed by underscores (such as linux) are defined in gnu\* modes. - The default setting for trigraphs in gnu\* modes is `OFF`. Trigraphs can be enabled by the `-trigraphs` option. - The parser recognizes asm and typeof as keywords in the gnu\* modes. The `__asm__` and `__typeof__` variants are recognized in all modes. - Arrays that are VLAs according to the standard, but which can be constant-folded by the compiler front end, are treated as fixed size arrays. For example, `int X[(1, 2)];` is technically a VLA. The c\* modes are strictly compliant and treat these as VLAs. - The Apple blocks extension is recognized by default in gnu\* modes on some platforms. It can be enabled in any mode with the `-fblocks` option. The \*89 and \*99 modes have the following differences: - The default setting for \*99 modes is to implement inline as specified in C11, while the \*89 modes implement the GNU version. The `__gnu_inline__` attribute can be used to override the default for individual functions. - Digraphs are not recognized in c89 mode. - The scope of names defined in a `for`, `if`, `switch`, `while`, or `do` statement is different. For example, `if ((struct x {int x;}\*)0) {}`. - `__STDC_VERSION__` is not defined in \*89 modes. - `inline` is not recognized as a keyword in c89 mode. - `restrict` is not recognized as a keyword in \*89 modes. - Commas are allowed in integer constant expressions in \*99 modes. - Arrays that are not `lvalues` are not implicitly promoted to pointers in \*89 modes. - Some warnings are different. c94 mode is identical to c89 mode except that digraphs are enabled in c94 mode. ### GCC extensions not yet implemented The LLVM compiler tries to be compatible with GCC as much as possible, but the following GCC extensions are not yet implemented in the LLVM compiler: - `#pragma weak` will probably be implemented at some point in the future, at least partially. - Decimal floating (`_Decimal32`, and so on) and fixed-point types (`_Fract`, and so on) - No one has expressed interest in this extension yet; currently, it is not clear when they will be implemented. - Nested functions - A complex feature that is infrequently used, so it is unlikely to be implemented soon. - Global register variables - Requires additional LLVM back-end support, so this extension is unlikely to be implemented soon. - Static initialization of flexible array members - A rarely used extension, but it could be implemented pending user demand. - `builtin_va_arg_pack` and `builtin_va_arg_pack_len` - Rarely used, but they might be implemented in potentially interesting places such as the `glibc` headers, pending user demand. Because the LLVM compiler pretends to be like GCC 4.2 and this extension was introduced in GCC 4.3, the `glibc` headers currently do not try to use this extension with the LLVM compiler. - Forward-declaring function parameters - This extension has not shown up in any real-world code yet, though, so it might never be implemented. ### Intentionally unsupported GCC extensions The LLVM compiler intentionally does not implement the following GCC extensions: - Variable-length arrays in structures This extension is not implemented for several reasons: > > > - It is tricky to implement > - The extension is completely undocumented > - The extension appears to be rarely used The LLVM compiler supports flexible array members (arrays with a zero or unspecified size at the end of a structure). - An equivalent to GCC’s fold This extension implies that the LLVM compiler does not accept some constructs GCC might accept in contexts where a constant expression is required, such as `x-x`, where `x` is a variable. - `__builtin_apply` and related attributes This extension is extremely obscure and difficult to reliably implement. ### Return of a function frame address `__builtin_return_address` returns the return address of the current function or one of its callers. A value of 0 yields the return address of the current function. Calling this function with a nonzero argument can have unpredictable effects, including crashing the calling program. ### Lvalue casts Old versions of GCC permit casting the left-hand side of an assignment to a different type. The LLVM compiler produces an error for code like this: lvalue.c:2:3: error: assignment to cast is illegal, lvalue casts are not supported (int*)addr = val; ^~~~~~~~~~ ~ Copy to clipboard To fix this problem, move the cast to the right-hand side. For example: addr = (float *)val; Copy to clipboard ### Jumps to within block variable scope The LLVM compiler disallows jumps into the scope of a `__block` variable. Variables marked with `__block` require special runtime initialization. A jump into the scope of a `__block` variable bypasses this initialization, leaving the variable’s metadata in an invalid state. Consider the following code fragment: int fetch_object_state(struct MyObject *c) { if (!c->active) goto error; __block int result; run_specially_somehow(^{ result = c->state; }); return result; error: fprintf(stderr, "error while fetching object state"); return -1; } Copy to clipboard GCC accepts this code, but it produces code that typically crashes when the result goes out of scope if the jump is taken. (It is possible for this bug to go undetected, because it often does not crash if the stack is fresh; that is, it is still zeroed.) Therefore, the LLVM compiler rejects this code with a hard error: t.c:3:5: error: goto into protected scope goto error; ^ t.c:5:15: note: jump bypasses setup of __block variable __block int result; ^ Copy to clipboard The fix is to rewrite the code so it does not require jumping into the scope of a `__block` variable; for example, by limiting that scope: { __block int result; run_specially_somehow(^{ result = c->state; }); return result; } Copy to clipboard ### Non-initialization of block variables In the following example, the variable `x` is used before it is defined: int f0() { __block int x; return ^(){ return x; }(); } Copy to clipboard By an accident of implementation, GCC and `llvm-gcc` unintentionally always zero any initialized `__block` variables. However, any programs that depend on this behavior relies on unspecified compiler behavior. Programs must explicitly initialize all local block variables before they are used, as with other local variables. The LLVM compiler does not zero-initialize local block variables—thus any programs that rely on such behavior will most likely break when built with this compiler. ## C++ compatibility This section describes common compatibility and portability issues with LLVM C++. The types in C++ code that are compiled with `-std=c++11` are not inter-operable with the following: - Types in C++ code that are compiled with `-std=c++03` - Code compiled with no `-std=` specification For example, the `std::string` from a C++03 compilation is unrelated to a `std::string` from a C++11 compilation, and the two `std::string` types cannot be easily converted to each other. ### Variable-length arrays GCC and C11 allow the size of an array to be determined at runtime. This extension is not permitted in standard C++. However, the LLVM compiler supports such variable length arrays in very limited circumstances for compatibility with GNU C and C11 programs. - The element type of a variable length array must be a *plain old data* (POD) type. It cannot have any user-declared constructors or destructors, any base classes, or any members of non-POD type. All C types are POD types. - Variable length arrays cannot be used as the type of a non-type template parameter. If the code uses variable length arrays in a manner that the LLVM compiler does not support, several methods are available to fix the code: - Replace the variable length array with a fixed-size array if you can determine a reasonable upper bound at compile time. Sometimes this is as simple as changing `int size = ...;` to `const int size = ...;` (if the initializer is a compile-time constant). - Use `std::vector` or some other suitable container type. - Allocate the array on the heap instead using new `Type[];` remember to delete it using `delete[]`. ### Unqualified lookup in templates Some versions of GCC accept the following invalid code: template T Squared(T x) { return Multiply(x, x); } int Multiply(int x, int y) { return x * y; } int main() { Squared(5); } Copy to clipboard The LLVM compiler flags this code with the following messages: my_file.cpp:2:10: error: call to function 'Multiply' that is neither visible in the template definition nor found by argument-dependent lookup return Multiply(x, x); ^ my_file.cpp:10:3: note: in instantiation of function template specialization 'Squared' requested here Squared(5); ^ my_file.cpp:5:5: note: 'Multiply' should be declared prior to the call site int Multiply(int x, int y) { ^ Copy to clipboard The C++ standard states that unqualified names such as `Multiply` are looked up in two ways: - First, the compiler performs an *unqualified lookup* in the scope where the name was written. For a template, this means the lookup is done at the point where the template is defined, not where it is instantiated. Because `Multiply` has not been declared yet at this point, unqualified lookup will not find it. - Second, if the name is called like a function, the compiler also does an argument-dependent lookup (ADL). In an ADL, the compiler looks at the types of all the arguments to the call. When it finds a class type, it looks up the name in that class’s namespace. The result is all the declarations it finds in those namespaces plus the declarations from unqualified lookup. However, the compiler does not do an ADL until it knows all the argument types. In the example code, Multiply is called with dependent arguments, so an ADL is not done until the template is instantiated. At that point, the arguments both have type int, which does not contain any class types, and so the ADL does not look in any namespaces. Because neither form of lookup found the declaration of `Multiply`, the code does not compile. Following is another example that uses overloaded operators, which obey very similar rules. #include template void Dump(const T& value) { std::cout << value << "\n"; } namespace ns { struct Data {}; } std::ostream& operator<<(std::ostream& out, ns::Data data) { return out << "Some data"; } void Use() { Dump(ns::Data()); } Copy to clipboard Again, the LLVM compiler flags this code with the following messages: my_file2.cpp:5:13: error: call to function 'operator<<' that is neither visible in the template definition nor found by argument-dependent lookup std::cout << value << "\n"; ^ my_file2.cpp:17:3: note: in instantiation of function template specialization 'Dump' requested here Dump(ns::Data()); ^ my_file2.cpp:12:15: note: 'operator<<' should be declared prior to the call site or in namespace 'ns' std::ostream& operator<<(std::ostream& out, ns::Data data) { ^ Copy to clipboard As before, unqualified lookup did not find any declarations with the name, `operator<<`. Unlike before, the argument types both contain class types: - One type is an instance of the class template type, `std::basic_ostream` - The other is the type ns::Data that is declared in the example Therefore, the ADL looks in the std and `ns` namespaces for an `operator<<`. Because one of the argument types was still dependent during the template definition, the ADL is not done until the template is instantiated during `Use`, which means the `operator<<` it finds has already been declared. Unfortunately, it was declared in the global namespace, not in either of the namespaces that the ADL look in. Two methods exist to fix this problem: - Make sure the function you want to call is declared before the template that might call it. This is the only option if none of its argument types contain classes. Either move the template definition or function definition, or add a forward declaration of the function before the template. - Move the function into the same namespace as one of its arguments so that the ADL applies. ### Unqualified lookup into dependent bases of class templates Some versions of GCC accept the following invalid code: template struct Base { void DoThis(T x) {} static void DoThat(T x) {} }; template struct Derived : public Base { void Work(T x) { DoThis(x); // Invalid! DoThat(x); // Invalid! } }; Copy to clipboard The LLVM compiler correctly rejects this code with the following errors (when `Derived` is eventually instantiated): my_file.cpp:8:5: error: use of undeclared identifier 'DoThis' DoThis(x); ^ this-> my_file.cpp:2:8: note: must qualify identifier to find this declaration in dependent base class void DoThis(T x) {} ^ my_file.cpp:9:5: error: use of undeclared identifier 'DoThat' DoThat(x); ^ this-> my_file.cpp:3:15: note: must qualify identifier to find this declaration in dependent base class static void DoThat(T x) {} Copy to clipboard As noted in [Unqualified lookup in templates](https://docs.qualcomm.com/doc/80-N3040-23/topic/language-compatibility.html#hccpp-lang-cpp-comp-unqual-lookup-temp-sub), unqualified names such as `DoThis` and `DoThat` are looked up when the `Derived` template is defined, not when it is instantiated. When looking up a name used in a class, you typically look into the base classes. However, you cannot look into the `Base` base class because its type depends on the template argument `T`, so the standard says to ignore it. As the LLVM compiler indicates, the fix is to tell the compiler that you want a class member by prefixing the calls with `this->`: void Work(T x) { this->DoThis(x); this->DoThat(x); } Copy to clipboard Alternatively, you can tell the compiler exactly where to look: void Work(T x) { Base::DoThis(x); Base::DoThat(x); } Copy to clipboard This approach works whether the methods are static or not, but be careful: if `DoThis` is virtual, calling it this way will bypass virtual dispatch. ### Incomplete types in templates The following code is invalid, but compilers are allowed to accept it: class IOOptions; template bool read(T &value) { IOOptions opts; return read(opts, value); } class IOOptions { bool ForceReads; }; bool read(const IOOptions &opts, int &x); template bool read<>(int &); Copy to clipboard According to the standard, types that do not depend on template parameters must be complete when a template is defined if they affect the program’s behavior. However, the standard also says that compilers are free to not enforce this rule. Most compilers enforce it to some extent; for example, it would be an error in GCC to write `opts.ForceReads` in the code example. In the LLVM compiler, the decision to enforce the rule consistently provides a better experience, but unfortunately, it also results in some code that is rejected when other compilers will accept it. ### Templates with no valid instantiations The following code contains a typo: `innit()` was written instead of `init()`. template class Processor { ... void init(); ... }; ... template void process() { Processor processor; processor.innit(); // <-- should be 'init()' ... } Copy to clipboard Unfortunately, the compiler cannot flag this mistake as soon as it detects it. Inside a template, assumptions cannot be made about dependent types such as `Processor`. Suppose that later in this file you add an explicit specialization of `Processor`, like the following example: template <> class Processor { void innit(); }; Copy to clipboard The program will work, but only if you instantiate `process()` with `T = char*`. Therefore, it is hard, and sometimes impossible, to diagnose mistakes in a template definition before it is instantiated. The standard states that a template with no valid instantiations is ill-formed. The LLVM compiler tries to do as much checking as possible at definition time instead of instantiation time; not only does this produce clearer diagnostics, but it also substantially improves compile times when using precompiled headers. The downside to this philosophy is that the LLVM compiler sometimes fails to process files because they contain broken templates that are no longer used. The solution is simple: because the code is unused, remove it. ### Default initialization of const variable of class type The default initialization of a `const` variable of a class type requires a user-defined default constructor. If a class or structure has no user-defined default constructor, C++ does not allow you to default-construct a `const` instance of it. For example: class Foo { public: // The compiler-supplied default constructor works fine, so we // don't bother with defining one. ... } void Bar() { const Foo foo; // Error! ... } Copy to clipboard To fix this issue, define a default constructor for the class: class Foo { public: Foo() {} ... }; void Bar() { const Foo foo; // Now the compiler is happy. ... } Copy to clipboard ### Parameter name lookup Due to a bug in its implementation, GCC allows the redeclaration of function parameter names within a function prototype in C++ code. For example: void f(int a, int a); Copy to clipboard The LLVM compiler diagnoses this error (where the parameter name has been redeclared). To fix this problem, rename one of the parameters. Last Published: Oct 06, 2026 [Previous Topic Avoid using inline assembly](https://docs.qualcomm.com/bundle/publicresource/80-N3040-23/topics/coding-practices.md) [Next Topic Language extensions](https://docs.qualcomm.com/bundle/publicresource/80-N3040-23/topics/language-extensions.md)