Clang was architected as a library-based, modular compiler around a high-fidelity AST to overcome GCC's deliberate lack of modular APIs and lossy parser constant-folding. (2:23)
In heavily templated C++11 benchmarks, Clang compiles roughly twice as fast as GCC, while the LLVM project targets link-time bottlenecks through a clean-slate linker project, lld. (53:18)
Compiler-assisted dynamic analysis tools like AddressSanitizer (ASan) outperform instruction-emulation tools like Valgrind by instrumenting LLVM IR directly, running with low overhead and monitoring stack, heap, and global variables. (40:41)
An unexpected interaction between C++11 constexpr, aggregate initialization, and C++98 legacy rules allows aggregate integer zero evaluations to silently match pointer overloads over class conversions. (47:53)
Detailed notes
## Background
This presentation was uploaded to YouTube by aghuttun on January 18, 2013, from the GoingNative 2012 conference. It features Chandler Carruth delivering a talk titled 'Clang: Defending C++ from Murphy's Million Monkeys.'
## TL;DR
Chandler Carruth outlines the architectural origins and core design philosophy of Clang and LLVM, emphasizing how high-fidelity ASTs and modular library designs overcome the tooling and diagnostic limitations of legacy compilers like GCC. The presentation walks through a vast spectrum of compiler-driven safety mechanisms, spanning caret diagnostics, intelligent typo recovery, and proactive warnings for syntactically valid yet buggy C++ patterns such as operator precedence traps, silent 64-bit integer overflows, and accidental function declarations. Carruth also demonstrates deep static analysis through thread-safety annotations, compiler-instrumented dynamic tools like AddressSanitizer and ThreadSanitizer, and explores emerging C++11 edge cases alongside performance efforts surrounding compile times and linking.
## Atmosphere & style
The presentation is energetic, witty, and engaging, balancing sharp developer humor with deep technical rigour. Carruth speaks with conversational authority, candidly recounting real-world production disasters and architectural frustrations to illustrate Murphy's Law in massive C++ codebases. Watching the talk feels like an entertaining yet pragmatic masterclass, drawing audible amusement and shared recognition from an audience of seasoned native systems programmers.
## Bulletin
- [02:23](https://www.youtube.com/watch?v=NURiiQatBXA&t=143s) Clang was architected as a library-based, modular compiler around a high-fidelity AST to overcome GCC's deliberate lack of modular APIs and lossy parser constant-folding.
- [53:18](https://www.youtube.com/watch?v=NURiiQatBXA&t=3198s) In heavily templated C++11 benchmarks, Clang compiles roughly twice as fast as GCC, while the LLVM project targets link-time bottlenecks through a clean-slate linker project, lld.
- [40:41](https://www.youtube.com/watch?v=NURiiQatBXA&t=2441s) Compiler-assisted dynamic analysis tools like AddressSanitizer (ASan) outperform instruction-emulation tools like Valgrind by instrumenting LLVM IR directly, running with low overhead and monitoring stack, heap, and global variables.
- [47:53](https://www.youtube.com/watch?v=NURiiQatBXA&t=2873s) An unexpected interaction between C++11 constexpr, aggregate initialization, and C++98 legacy rules allows aggregate integer zero evaluations to silently match pointer overloads over class conversions.
## Key takeaways
- [00:00](https://www.youtube.com/watch?v=NURiiQatBXA&t=0s) Traditional open-source compilers previously offered poor diagnostics and slow compilation compared to commercial tools like Visual C++.
- [02:23](https://www.youtube.com/watch?v=NURiiQatBXA&t=143s) GCC deliberately avoided modular APIs to prevent integration with non-free software, prompting Apple and the LLVM community to build Clang as an extensible, library-based compiler.
- [08:34](https://www.youtube.com/watch?v=NURiiQatBXA&t=514s) At enterprise scale across thousands of developers, subtle language pitfalls and undefined behaviors inevitably trigger catastrophic production outages under Murphy's Law.
- [11:47](https://www.youtube.com/watch?v=NURiiQatBXA&t=707s) Clang improves diagnostic clarity using caret indicators, source range highlighting, macro expansion traces, and concise root-cause detection for complex variadic template errors.
- [14:48](https://www.youtube.com/watch?v=NURiiQatBXA&t=888s) Intelligent semantic recovery and typo correction enable the compiler to keep parsing translation units accurately without cascading phantom errors.
- [18:31](https://www.youtube.com/watch?v=NURiiQatBXA&t=1111s) Clang prioritizes warnings for valid but mistaken code patterns, such as invoking sizeof on pointers or mixing operator precedence, catching dormant bugs across codebases like Google Chrome.
- [21:24](https://www.youtube.com/watch?v=NURiiQatBXA&t=1284s) Intermediate C++ integer arithmetic rules evaluate based on operand types rather than destination types, resulting in silent signed overflow even when assigning to 64-bit containers.
- [33:05](https://www.youtube.com/watch?v=NURiiQatBXA&t=1985s) Inline static analysis and domain annotations (e.g., guarded_by) permit the compiler to verify multithreaded lock safety invariants across branching control flows.
- [37:07](https://www.youtube.com/watch?v=NURiiQatBXA&t=2227s) Dynamic instruction simulation through Valgrind imposes a 20x to 30x slowdown and fails to track stack-allocated variables, whereas compiler IR instrumentation via ASan enables near-native execution.
- [43:20](https://www.youtube.com/watch?v=NURiiQatBXA&t=2600s) Modern C++11 features introduce subtle traps, such as deducing std::initializer_list when using auto with braced lists, creating potential dangling references.
- [45:36](https://www.youtube.com/watch?v=NURiiQatBXA&t=2736s) Compiler developers must aggressively implement diagnostics for C++11 edge cases and move semantics to prevent developer confusion and protect adoption.
- [53:18](https://www.youtube.com/watch?v=NURiiQatBXA&t=3198s) Slow link times stem from archaic linker formats and decades-old designs rather than intrinsic algorithmic barriers, spurring development of the LLVM lld project.
- [57:04](https://www.youtube.com/watch?v=NURiiQatBXA&t=3424s) Clang development on Windows faces community bandwidth challenges due to the strength of Visual C++, requiring more external Windows patches and dedicated testing infrastructure.
- [59:02](https://www.youtube.com/watch?v=NURiiQatBXA&t=3542s) To prevent AST corruption and parser instability, Clang attaches fix-it hints to primary diagnostics only when an error occurs and the parser can safely recover by assuming the suggested edit.
## Detailed walkthrough
### [00:00](https://www.youtube.com/watch?v=NURiiQatBXA&t=0s) Introduction and the Genesis of Clang
The session begins with an introduction highlighting Chandler Carruth as an embodiment of the next generation of C++ leaders, noting that he owns Clang at Google. Carruth takes the stage, thanking conference organizers Herb Sutter and Charles Torre for the opportunity to introduce Clang to a broader audience who might not encounter it in their daily development routines. To gauge the audience's familiarity, he conducts a quick show of hands, asking who has used Clang even once, who has used it in the past month, and jokingly asking who started using it solely because of this talk.
Carruth addresses the foundational question: why invest immense effort into building another C++ compiler when several already exist? The motivation stems directly from the frustrating state of traditional open-source compilers at the time, particularly when compared to commercial offerings like Microsoft's Visual C++. In older compilers, minor typographical mistakes such as missing or misplaced curly braces resulted in cryptic diagnostic output that simply printed token dumps across the terminal. As Carruth points out, having raw tokens listed provides no actionable help for correcting mistakes. Beyond poor error reporting, compile times were notoriously sluggish, leaving engineers waiting endlessly for builds to complete.
[02:23](https://www.youtube.com/watch?v=NURiiQatBXA&t=143s) Tooling Frustrations and the LLVM / Clang Solution
The most severe limitation of existing open-source infrastructure went beyond slow speeds and poor diagnostics: developers desperately needed tools that shared the compiler's native understanding of C++. Whether building IDE integration, static analyzers, or refactoring engines, re-implementing a C++ parser from scratch for every separate tool is counterproductive. However, attempts to use GCC's front-end as a reusable library hit an intentional, political roadblock. Carruth highlights a direct post from the GCC mailing list where Richard Stallman explicitly defined GCC's architectural philosophy:
> “One of the main goals for GCC is to prevent any parts of it from being used together with non-free software. Thus, we have deliberately avoided many things that might possibly have the effect of facilitating such usage.”
Beyond ideological obstacles, GCC was technically unsuited to power refactoring or code analysis tools. Carruth recounts attempting to run a basic source-analysis tool against simple C code containing an expression like `+ 0`. GCC's parser performed trivial constant folding on the fly, discarding the `+ 0` tokens entirely before generating a semantic model. Because the parser mangled source reality, the tool could not understand what was actually written. As Carruth sharply puts it:
> “You are making it far too easy to point the gun at my foot. You're making you're practically pulling the trigger for me.”
Faced with these insurmountable barriers, Chris Lattner and the LLVM community at Apple pursued the open-source remedy. Rather than fighting uncooperative internal designs, Chris Lattner resolved the problem directly:
> “I know how to solve this problem. I'll write a new one.”
- GCC's parser performed aggressive early constant folding, discarding syntactic elements such as `+ 0` directly on the floor.
- Richard Stallman's explicit strategy was to avoid clean APIs or modular library interfaces to discourage integration with proprietary software.
- Clang was engineered from the ground up as a set of modular libraries around a high-fidelity abstract syntax tree (AST).
[06:08](https://www.youtube.com/watch?v=NURiiQatBXA&t=368s) Current State of Clang and C++11 Support
Carruth presents an update on Clang's maturity. For C++98, Clang is a fully functional, production-ready, highly compliant compiler that runs smoothly on both Linux and Mac OS X (though Windows support remains an active work in progress). The compiler is modular, factored into libraries, and already powers a wide suite of developer tools.
Furthermore, Clang’s velocity on the newly ratified C++11 standard had far outpaced expectations. When Carruth audited the codebase, he discovered that only a handful of C++11 features remained incomplete. While features like initializer lists, the final parts of generalized constant expressions (`constexpr`), and lambdas were still missing in the current release, active development made it likely that all three would land in the very next release.
- Features slated for the upcoming release: generalized constant expressions (`constexpr`), initializer lists, and lambdas.
- Features expected to take longer: unrestricted unions, inheriting constructors, and comprehensive C++11 atomics and concurrency models.
- Concurrency and memory model semantics remain an active, cross-industry learning process requiring substantial engineering over time.
- Clang embraces an open-source release model of launching early and refining edge cases and bugs iteratively in public.
[08:34](https://www.youtube.com/watch?v=NURiiQatBXA&t=514s) Murphy's Law and the Million Monkeys
Shifting from Clang's origin story to its overarching mission, Carruth introduces the central challenge confronting Google and large engineering organizations: Murphy's Law. In massive codebases worked on by thousands of developers, every theoretical failure mode eventually becomes an active outage.
Programming in a language as complex and expansive as C++ magnifies these risks exponentially. When millions of lines of code interact, accidental misuses of language features, subtle undefined behaviors, and unexpected compiler transformations inevitably trigger catastrophic bugs. The remainder of the lecture sets out to explore how Clang acts as an automated defense system—protecting C++ developers from Murphy's Law and the hazards introduced by sheer scale.
[09:17](https://www.youtube.com/watch?v=NURiiQatBXA&t=557s) Defending Against Murphy and the Million Monkeys
Chandler Carruth cautions that without comprehensive safety mechanisms, programming in C++ can quickly spiral out of control. In Carruth's framing, Murphy's Law is not an indifferent law of nature: “Murphy is not a passive player here. He is actively seeking out the flaws in your code.” Displaying an image from a UK television series featuring a fierce-looking character, Carruth jokes that this angry persona represents the disaster waiting to strike when developers let their guard down. C++ itself provides strong safety capabilities when used carefully by a solitary developer, but software engineering does not happen in isolation. Instead, development organizations employ hundreds or thousands of programmers simultaneously hammering away on keyboards—what Carruth playfully dubs an expanding herd of "code monkeys" collaborating on complex codebases.
```mermaid
flowchart TD
accTitle: The Murphy and Scale Problem in C++
accDescr: Diagram showing how multiple developers amplify simple errors into widespread productivity disasters under Murphy's law.
subgraph Scale ["Developer Scale"]
D["Million Monkeys<br/>(Developers typing fast)"] --> E["Simple Mistakes & Typos"]
end
subgraph Threat ["Active Adversary"]
E --> M["Angry Murphy<br/>(Actively seeking flaws)"]
end
subgraph Impact ["Consequences"]
M --> P["Developer Disasters<br/>(Missed ship dates & lost productivity)"]
end
```
These disasters rarely involve dramatic physical catastrophes like crashing fighter jets or exploding oil rigs. In commercial software development, disaster usually takes the form of missed shipping deadlines, wasted engineering cycles, and pervasive confusion caused by obscure compiler messages. Because ordinary developers constantly battle these small, recurring friction points, compiler tooling must actively support them. To defend against Murphy and tame the chaos of large developer teams, Clang returned to software engineering fundamentals by completely rethinking compiler diagnostics.
- Murphy is framed as an active, aggressive adversary hunting down oversights in software rather than a passive annoyance.
- Scaling developer headcounts drastically increases the probability of minor mistakes becoming widespread blockers.
- Disasters in day-to-day engineering are measured in lost productivity, misunderstood source code, and slipped release schedules.
---
[11:47](https://www.youtube.com/watch?v=NURiiQatBXA&t=707s) Clear Diagnostics: Unwinding Template Instantiations and Macro Expansions
When developers type quickly under pressure, invalid C++ code inevitably creeps into translation units. Historically, C++ compilers responded with opaque walls of text. Clang takes a structural approach to diagnostics by providing caret diagnostics, highlighting exact source ranges, and explicitly detailing the contextual chain that caused an invalid construct. Carruth illustrates this with an example of an array declared with a negative size resulting from an expression like `n - 5`. Rather than merely printing that an array size is invalid, Clang walks backward through the template instantiation stack. It shows that the failure occurred inside a template class instantiation where the non-type template parameter `n` was instantiated with the value `4`—immediately clarifying why the array size resolved to `-1`.
```mermaid
flowchart TD
accTitle: Clang Contextual Diagnostic Unwinding
accDescr: Flowchart illustrating how Clang walks up the instantiation and macro expansion hierarchies to explain root-cause errors.
subgraph MacroStack ["Macro Diagnostic Walkback"]
M1["Macro Invocation in Source"] --> M2["Intermediate Macro Expansion Step"]
M2 --> M3["Final Preprocessed Expansion Point"]
end
subgraph TemplateStack ["Template Diagnostic Walkback"]
T1["Class Template Definition"] --> T2["Template Argument Substitution (e.g., n = 4)"]
T2 --> T3["Invalid Concrete Construct (e.g., Array of size -1)"]
end
```
This structural transparency becomes even more critical when untangling preprocessor macros. Echoing Herb Sutter's aspiration for a future C++ without preprocessor macros, Carruth laments that real-world developers are still forced to consume legacy macro-heavy libraries. When a macro expansion triggers a compiler error, Clang systematically traces each substitution step through the preprocessor pipeline. It visually isolates the specific macro argument that caused the failure, demonstrates where that argument was forwarded into nested macro layers, and shows its final expanded form. “This is incredibly powerful for understanding exactly how bad code got into your code,” Carruth emphasizes, giving developers immediate insight into code they never directly authored.
- Clang explicitly identifies the root reason for invalid declarations, such as instantiating a template parameter with `4` when `42` was intended.
- Diagnostic messages underline and visually pinpoint erroneous sub-expressions rather than merely reporting file line numbers.
- Macro diagnostics preserve full preprocessor expansion history, exposing hidden intermediate expansions across nested macros.
---
[14:48](https://www.youtube.com/watch?v=NURiiQatBXA&t=888s) Intelligent Typo Correction and Resilient AST Recovery
Beyond structural template and macro errors, everyday programming is plagued by mundane typographical mistakes. Carruth confesses to being a frequent offender and demonstrates how Clang applies semantic analysis to recover from typos automatically. In one example, source code references an identifier named `base` in a constructor's member initialization list, where both a static member variable and a base class type share that name. Because the C++ grammar strictly prohibits static variables inside member initializers, Clang determines that the programmer must have intended the base class type. Crucially, after suggesting the fix, Clang continues parsing the remainder of the file under the assumption that the correct type had been provided.
Carruth highlights this recovery mechanism as a fundamental paradigm shift in compiler design: “This actually allows you to not send all of your error messages into head and just look at the first line because you're going to continue to get accurate and useful error messages throughout the file.” When multiple nested mistakes occur—such as omitting a namespace qualifier and subsequently misspelling a method name—Clang corrects the qualifier, assumes the repaired namespace scope, and successfully locates the misspelled symbol deeper down the chain. Furthermore, Clang can optionally edit source files in place using fix-it hints if requested, removing the need for manual developer intervention.
- Clang evaluates semantic context (such as constructor initialization syntax) to disambiguate identical type and variable names during typo correction.
- Parsing recovery avoids generating cascading "phantom" errors, ensuring diagnostics later in the file remain accurate and meaningful.
- Multi-step typo correction can resolve missing namespace qualifiers and symbol misspellings in a single pass.
- Clang fix-it hints can automatically apply suggested edits directly to source files upon user request.
---
[17:17](https://www.youtube.com/watch?v=NURiiQatBXA&t=1037s) Resolving Andrei Alexandrescu's "Oscar Wilde" Pack Expansion Challenge
Transitioning toward bugs in code that is syntactically valid yet functionally broken (such as legacy C-style API misuse), Carruth briefly halts his progression as he spots a slide he had added at the last minute: “Sorry, my slides are betraying me. Bear with me. So, Oscar Wilde, Oscar Wilde, let's make sure we cover Andrei's Oscar Wilde diagnostic because he had a legitimate concern.” During an earlier session at the conference, Andrei Alexandrescu had joked about needing an Oscar Wilde-length novel of compiler diagnostics to diagnose complex variadic template errors—specifically when expanding multiple parameter packs simultaneously.
Carruth presents Clang's handling of simultaneous variadic template parameter pack expansions whose lengths do not align. In conventional compilers, mismatched pack expansions often trigger unreadable screens of cascading template instantiation errors. Clang, by contrast, detects the mismatch immediately at the expansion site and outputs a clear, concise diagnostic stating that the packs cannot be expanded together because their sizes differ (for example, pack lengths of two versus three). Carruth emphasizes that addressing these advanced variadic metaprogramming edge cases with concise messages was important enough to adjust his slide deck right before taking the stage.
- Simultaneous expansion of multiple parameter packs requires identical pack lengths, making mismatches notoriously difficult to diagnose in complex templates.
- Rather than dumping dense template backtraces, Clang isolates the root arithmetic mismatch (e.g., comparing pack lengths of 2 against 3).
- The diagnostic was implemented and showcased specifically in response to Andrei Alexandrescu's critique of variadic template diagnostic bloat.
[18:31](https://www.youtube.com/watch?v=NURiiQatBXA&t=1111s) The Bread and Butter: Warnings for Valid but Mistaken Code
Chandler Carruth emphasizes that compiler diagnostics are not merely about hard compile-time errors. In Clang, warnings are considered the real workhorses: “We love warnings in Clang. This is this is the bread and butter of Clang diagnostics are warnings.” The fundamental distinction is that these diagnostics target code that is entirely legal under the C++ standard. The standard specifies exactly what will occur at runtime, yet the compiler can inspect the surrounding context and recognize with near-total certainty that the programmer did not intend what they wrote.
A prime example is calling `sizeof` on a pointer variable (such as `sizeof(s)`) when the developer is trying to initialize or manipulate the underlying struct or buffer that `s` points to. Because the compiler tracks the AST and type information, it knows `s` is a pointer, detects that `sizeof` evaluates only to the pointer’s size rather than the pointee's structure size, and provides a fix-it hint. Carruth notes that this specific diagnostic uncovered dormant bugs across numerous codebases, including the Google Chrome browser. The danger of these defects is that they frequently pass basic manual testing because they partially succeed: “And that's the most frightening part is that oftentimes it will mostly work. It will initialize some of your data just not all of it.”
```mermaid
flowchart TD
accTitle: Compiler Semantic Diagnostic Flow
accDescr: Flowchart illustrating how Clang determines whether code is valid standard C++ but semantically unintended.
subgraph Analysis["Static Code Inspection"]
A["Valid C++ Source"] --> B{"Matches Language Grammar?"}
B -->|"Yes"| C{"Examines Program Context & Meaning"}
end
subgraph Decision["Diagnostic Output"]
C -->|"Definite semantic bug"| D["Clang Warning + Fix-it Hint"]
C -->|"Standard behavior intended"| E["Silent Clean Compilation"]
end
```
[20:02](https://www.youtube.com/watch?v=NURiiQatBXA&t=1202s) Catching Semantic Mistakes: Out-of-Bounds and Uninitialized Variables
Clang pushes diagnostic checks into everyday source patterns where valid syntax produces useless or dangerous operations. When code contains expressions with no side effects or fails to return a value from a non-void function, Clang flags the semantic absurdity rather than mechanically accepting the standard-compliant tokens. Similarly, Clang evaluates constant expressions and array limits at compile time to catch off-by-one errors—such as referencing index `100` in a 100-element container where valid elements only run up to index `99`.
The analysis also tracks control flow to catch uninitialized variables before execution ever occurs. While humans reviewing code can easily lose track of branches where an initialization never happened, the compiler's data-flow analysis can prove it definitively. As Carruth explains, “It's trivial to prove to a compiler although perhaps not to a human that in fact you never initialize X you never came close to initializing X.” Clang pinpoints where the uninitialized variable is first read and explains how to resolve the issue or explicitly initialize it.
[21:24](https://www.youtube.com/watch?v=NURiiQatBXA&t=1284s) Chandler's Own Bug: Integer Arithmetic and 8 Gigabytes
Carruth confesses that one of his favorite diagnostics arose from a bug he personally authored and had reviewed by multiple engineers who all believed it was correct. The code was designed to compute a size allocation for an 8-gigabyte buffer. To ensure safety, the developers explicitly used a large, portable 64-bit integer type to hold the resulting value, believing that having sufficient storage prevented any potential overflow.
However, C++ expression evaluation rules do not determine the type of an arithmetic expression by the type of the variable receiving the assignment. The intermediate multiplication was performed using standard 32-bit signed integers, which overflowed into undefined behavior long before assignment occurred. Carruth quips about undefined behavior: “Well, C++'s integer arithmetic rules are strange. They are not what you thought they were. And so this does something very surprising. This actually can, you know, order you a burrito if it likes.” Clang tackles this by inspecting constant integer math across bit widths, issuing a diagnostic that details the exact value the user attempted to compute, the number of bits required, how many bits were actually available, and where the overflow took place. Carruth emphasizes the underlying rule: “It doesn't matter what you're storing the value into. You have to actually compute the right value first.”
[23:02](https://www.youtube.com/watch?v=NURiiQatBXA&t=1382s) The Comma Typo: Accidentally Declaring a Function
Presenting what he calls his "favorite typo," Carruth displays a snippet of code that looks clean and natural at a glance—so deceptive that it remained undetected in production code for years. The audience is challenged to spot the flaw, which hinges on a single character: an accidental comma placed instead of a semicolon between an object definition and what appears to be a function call.
Instead of executing the function, the comma causes the compiler to parse the trailing expression as a local function declaration sharing the preceding type. Carruth explains the confusion: “How many things have you declared here? Two. That's a function, not a call.” Because declaring a local function signature is fully legal in C++, the translation unit compiled cleanly while the target function was never invoked. Clang solves this by identifying that a comma precedes what looks like an in-scope callable function declaration, checks whether a function matching that signature exists to call, and tells the developer that they mistakenly declared a function when they meant to separate statements with a semicolon.
[24:22](https://www.youtube.com/watch?v=NURiiQatBXA&t=1462s) Null Pointers and Charging Customers Money
Carruth walks through an example involving financial transaction logic: state updates tracking whether or not a customer should be billed. Incorrect behavior here is catastrophic for a business: “Charging money is kind of burning things for companies, right? Like you don't want to do that the wrong way.”
In the example, a bug meant to toggle a boolean condition instead evaluated down to setting a raw pointer to null due to legacy C-style conversion rules. When engineers were shown the warning, they often reacted with outright denial: “This surprise many engineers that I talk to. This is setting a pointer. Where is null in this source code? I would have people challenge me. There's no null here.” This hazard illustrates why modern C++ conventions and the introduction of `nullptr` in C++11 are vital: they eliminate the silent, unsafe conversions of literal zero into pointer contexts that plague older codebases.
[25:40](https://www.youtube.com/watch?v=NURiiQatBXA&t=1540s) Assignment in Conditions and the Escape Hatch Pattern
The discussion turns to one of the most notorious mistakes in C-family languages: accidentally using the assignment operator `=` instead of the equality operator `==` inside an `if` condition. Carruth observes: “I have never seen a classroom of students learning C++ where at least a quarter of them didn't make this mistake in the first month.” Because an assignment in a conditional test is valid syntax that simply tests the assigned value, both Clang and GCC aggressively diagnose it.
However, because assignment within an `if` predicate is occasionally an intentional idiom (such as extracting a handle and checking for non-null in one step), the compiler must provide an intentional escape hatch without stripping expressivity from the language:
- **Intentional Syntax:** Wrapping the assignment in redundant outer parentheses—`if ((x = y))`—explicitly signals to the compiler that the assignment was deliberate.
- **Compiler Suppression:** Seeing the outer parentheses, Clang silences the warning: “If you wrap extra parentheses, completely pointless extra parentheses around this, the compiler's like, 'Okay, okay, I'll leave you alone, right? You get what you asked for, right?'”
- **The Inverse Bug:** Carruth notes that after developers were trained to add extra parentheses to suppress assignment warnings, a new issue arose: programmers began accidentally making the reverse typo—double-parenthesizing accidental equality checks or inverted logic—which compilers initially failed to diagnose.
[27:57](https://www.youtube.com/watch?v=NURiiQatBXA&t=1677s) Operator Precedence: Logical Algebra and Forced Parenthesization
Chandler Carruth notes that after addressing blessed assignment syntax in conditions, another major area requiring source changes to clarify intent involves operator precedence. While one might expect experienced C++ developers to understand how operators bind, code mixing logical operators is astonishingly prone to defects. Carruth observes that programmers rarely think about Boolean expressions in terms of formal Boolean algebra; as a result, they consistently misjudge which precedence relationships hold between logical AND (`&&`) and logical OR (`||`), writing expressions whose actual runtime behavior completely diverges from their mental model.
To put an end to this ubiquitous failure mode, Clang enforces a diagnostic rule forbidding ambiguous, unparenthesized mixing of logical operations. Whenever an implicit precedence relationship between logical OR and logical AND is detected, the compiler emits a warning accompanied by a descriptive diagnostic explaining the algebraic precedence rules, why the code might yield unexpected results, and how to resolve the ambiguity. Developers are explicitly required to add parentheses so their intent is indisputable. As Chandler Carruth puts it: “And we force you to explicitly group them so that we're certain what relations you want.”
- Logical operations follow formal algebraic precedence rules where `&&` takes precedence over `||`, but developers frequently misjudge the grouping.
- Clang emits a warning on any implicit precedence relations between logical OR and logical AND.
- The diagnostic message explains the underlying hazard and suggests explicit parenthesization to guarantee intended evaluation order.
---
[29:18](https://www.youtube.com/watch?v=NURiiQatBXA&t=1758s) The Addition and Streaming Operator Trap
Precedence surprises extend well beyond pure logic gates into everyday arithmetic and relational operations. Carruth presents a code snippet to the audience and asks for guesses on what the function returns, revealing the punchline: “X always X never anything other than X.” Engineers are routinely caught off guard by how addition interacts with comparison operators and conversions. Because C++ permits implicit conversions to `bool`, and relational operators evaluate directly to Boolean values, an unintended operator precedence cascades across an expression, silently altering evaluation order and operand types without producing a syntax error.
This flaw is particularly rampant when combined with stream insertion and bitwise shift operators (`<<` and `>>`). Developers streaming diagnostics to `std::cout` or `std::cerr` frequently mix arithmetic, logical conditions, and stream operations in single statements. Because these operators have well-defined semantics when integers appear on either side and stream operations produce expressions that can convert to Boolean contexts, subtle mis-groupings generate wildly unintended program behavior. Because these mistakes were found to be exceptionally common across large codebases, providing clear compiler warnings to intercept them became a high priority for the Clang team.
---
[30:19](https://www.youtube.com/watch?v=NURiiQatBXA&t=1819s) Pythonic Indentation Traps and Exhaustive Enum Switches
Formatting often tricks the human eye into seeing control flow that does not exist in C++. Carruth shows a visual typo where deceptive indentation makes a code block appear nested beneath a condition, joking: “Like like the seductiveness of Python's indentation just knows no bounds, right?” While a human code reviewer glides over the indentation assuming the block is guarded, Clang parses the actual grammar—catching misplaced semicolons or dangling statements—and warns the programmer that the indentation does not match the actual execution hierarchy.
```mermaid
flowchart TD
accTitle: Exhaustive Enum Switch Coverage in Clang
accDescr: Flowchart illustrating how Clang validates switch statements over enumerations, either requiring all cases or a default handler.
subgraph Input["Enum Switch Evaluation"]
A["switch (my_enum)"]
end
subgraph Branch["Coverage Analysis"]
B{"Is 'default:' present?"}
C["Enforce enumeration of all cases"]
D["Allow omitted enumerators"]
end
subgraph Outcome["Compiler Diagnostic"]
E["Warning: Missing enumerator (e.g., case Z)"]
F["Compilation passes"]
end
A --> B
B -->|"No"| C
B -->|"Yes"| D
C -->|"Omitted enumerator"| E
C -->|"All cases handled"| F
D --> F
```
The presentation then turns to switch statements over enumerations, a pattern notorious for maintenance bugs. When a developer expands a widely used `enum` by adding a new enumerator, finding every switch statement across a codebase that needs to handle that new case is nearly impossible by manual inspection alone. Clang solves this by checking switch statements that lack a `default` label, demanding that every single enumerator be explicitly handled. When an audience member interjects about additional flaws in his slide, Carruth laughs at his own code: “Every single example of code I have is buggy in more than one way, apparently.”
- Clang identifies misleading indentation where statement nesting contradicts C++ block structure.
- When switching over an `enum` without a `default` case, Clang insists on complete coverage of all declared values.
- If an enumerator (such as a newly added value like `Z`) is unhandled, Clang fires a warning identifying the missing case, facilitating safe library and API evolution.
---
[31:59](https://www.youtube.com/watch?v=NURiiQatBXA&t=1919s) Compile-Time Constants, Dead Code, and Semantic Bounds Checking
Carruth addresses idioms where compile-time constants dictate conditional execution, such as build-configuration flags or debug toggles. Developers often avoid preprocessor macros in favor of natural C++ code, checking constant flags inside regular `if` statements. However, these compile-time branch guards frequently mask code that is invalid or dangerous under other conditions.
When an array bounds check or an explicit guard clause relies on known constant values, Clang tracks the underlying semantics throughout the routine. For example, if a constant index like `44` is passed into an array operation past the declared bounds, Clang traces the constant values across the function and identifies that the out-of-bounds access is guaranteed to occur despite the surrounding control-flow logic. As Chandler Carruth argues: “Even though we seem to be handling things, we actually we're actually are trying to encode all the semantics here as closely as we can.”
---
[33:05](https://www.youtube.com/watch?v=NURiiQatBXA&t=1985s) Full-Blown Static Analysis and Thread Safety Annotations
Moving into complex code structures, Carruth explains that Clang’s warnings are no longer just shallow syntactic checks; the compiler executes full-blown static analysis directly during the compilation process. This deep analysis is designed to reason about the runtime behavior of the program and catch subtle defects that escape typical human code reviews—especially in concurrent programming involving shared global state and mutual exclusion. In multithreaded routines, functions often rely on preconditions, such as requiring a mutex to be held upon entry or ensuring a lock is not leaked across an exit or loop continuation. Because these invariants are traditionally invisible in the C++ type system, compilers could not enforce them.
```mermaid
flowchart LR
accTitle: Clang Thread Safety Static Analysis Pipeline
accDescr: Pipeline showing annotated C++ code being verified by Clang static analysis to produce concurrency diagnostics.
subgraph Annotations["C++11 Domain Annotations"]
A["guarded_by(mutex)"]
B["exclusive_locks_required(mutex)"]
end
subgraph Violations["Diagnosed Concurrency Defects"]
D["Unprotected variable read"]
E["Unlocked function call"]
F["Lock state mismatch across loop"]
G["Leaked lock on function exit"]
end
A --> C
B --> C
C --> D
C --> E
C --> F
C --> G
```
To bridge this gap, Clang leverages C++11 generalized attributes to annotate functions and data members with domain-specific requirements, such as specifying that a variable is guarded by a particular lock or that a routine requires exclusive locks. While Carruth acknowledges that standard generalized attribute syntax was still in active development in Clang at the time—forcing him to use GNU-style `__attribute__` syntax under the hood despite preferring cleaner C++11 syntax—the compiler uses these annotations to track lock state across every execution path. It flags reads without locks, unlocked calls, mismatched lock states across loop continuations, and forgotten unlocks before returns. As Chandler Carruth summarizes: “Every time we can extend the language to codify the semantics, we can actually get improved checking from our tools and especially our compilers.”
- Static analysis runs inline during compilation to evaluate program behavior and enforce semantic contracts.
- Clang utilizes thread-safety annotations like `guarded_by` and `exclusive_locks_required` on shared variables and synchronization primitives.
- The compiler traces control-flow paths to catch unprotected shared-memory reads, missing locks before function invocations, inconsistent locking across loop branches, and lock leaks upon return.
[37:07](https://www.youtube.com/watch?v=NURiiQatBXA&t=2227s) The Limits of Static Analysis and the Valgrind Trade-Off
Chandler Carruth transitions from compile-time enforcement to runtime verification by introducing dynamic analysis. While static analysis is undeniably powerful for establishing program invariants and strictly checking them across all possible execution paths regardless of runtime inputs, there are critical classes of invariants that compilers cannot easily enforce across an entire codebase pedantically. Carruth demonstrates this with a buggy code snippet containing two separate flaws: one is trivial to identify statically, while the other is significantly trickier because determining its presence requires tracking loop trip counts and detailed control flow. Even if that second bug were caught statically in an isolated snippet, minimal refactoring or code movement would quickly render static analyzers blind to it.
To catch these subtle failures, developers turn to tools that inspect the running program directly. Valgrind stands out as the canonical tool for this job. When pointed at the unoptimized example, Valgrind instruments the binary, runs it, and accurately diagnoses both defects: reading uninitialized memory and performing an out-of-bounds read past the end of an array. Carruth notes that while Valgrind’s raw terminal output can be cluttered with extraneous information, it succeeds in reporting precisely what went wrong.
```mermaid
flowchart TD
accTitle: Trade-offs of Dynamic Instrumentation in Valgrind
accDescr: Diagram illustrating how binary-level simulation in Valgrind causes performance overhead and misses stack-allocated bugs.
subgraph ValgrindApproach["Valgrind: Dynamic Binary Simulation"]
A["Target Binary"] --> B["Simulated CPU & Memory Engine"]
B --> C{"Check Operation"}
C -->|"Heap Allocation"| D["Tracked: Catches Buffer Overflows"]
C -->|"Stack Allocation"| E["Skipped: Fast Path Ignored"]
end
subgraph Limitations["Consequences"]
D --> F["Massive Runtime Overhead (20x-30x)"]
E --> G["Silent Failures on Simplified/Modern C++ Code"]
end
```
However, Valgrind suffers from deep architectural limitations. When the sample code is simplified by moving allocations onto the stack—making the code cleaner and less complex—Valgrind actually catches *fewer* bugs. Carruth stresses how damaging this inversion is: developers are constantly told to simplify code to eliminate bugs, yet doing so blinds their dynamic diagnostic tools. This breakdown occurs because Valgrind performs heavy binary simulation. Because simulating every hardware instruction is prohibitively costly, Valgrind is forced to make compromises to remain tractable: it deliberately avoids tracking operations intended to be lightweight, such as stack-allocated variables. Compounding the issue, its dynamic instrumentation introduces a brutal 20x to 30x runtime slowdown, often preventing large test suites or long-running applications from ever reaching the bug site to generate a report.
- Static invariants hold across every execution path, but dynamic tools observe failures that occur only along concrete runtime paths.
- Valgrind accurately catches uninitialized reads and heap buffer overruns, but emits substantial irrelevant diagnostic noise.
- Simplifying dynamic heap allocations into local stack allocations causes Valgrind to miss out-of-bounds errors entirely.
- Valgrind incurs an execution penalty of 20x to 30x due to full-system binary interpretation and simulation.
---
[40:41](https://www.youtube.com/watch?v=NURiiQatBXA&t=2441s) Compiler-Assisted Instrumentation: AddressSanitizer and Catch Undefined Behavior
Because post-compilation binary instrumentation is both excessively slow and blind to stack operations, Carruth advocates moving dynamic analysis directly inside the compiler. Clang is not just a syntax-checking front end; it owns code generation, meaning it can inject targeted, highly optimized dynamic checks right where they are needed. By generating instrumentation during intermediate representation (IR) lowering, the compiler creates binaries that run dramatically faster and maintain pinpoint accuracy across both stack and heap buffers. This philosophy drives LLVM’s AddressSanitizer (ASan) for memory safety and ThreadSanitizer (TSan) for data race detection. Carruth jokes that while he wanted to present ThreadSanitizer examples, the threading libraries paired with it at the time lacked C++11 `<thread>` support: “I was not going to write POSIX threads for this talk. That that would just be too—I couldn't get up here and actually project POSIX threads at you guys, like I would get run off the stage, rightfully so.”
Turning to current compiler capabilities, Carruth exhibits an out-of-range bitshift bug where an integer is shifted past the width of its type. Static analysis cannot reliably flag this when the computation is split across functions in different translation units or inside precompiled vendor libraries. To resolve this, Clang provides a dynamic instrumentation mode called `-fcatch-undefined-behavior`. With this switch active, Clang inserts minimal, low-overhead instructions directly into the binary. Rather than executing undefined operations and generating silent memory corruption or nonsensical calculations, the instrumented application immediately traps and crashes precisely at the instruction where the violation occurs.
Carruth emphasizes that compiler-assisted dynamic analysis represents the future of native debugging, though substantial engineering work remains. The starkest proof of this architecture’s advantage over traditional binary instrumentation is its real-world performance: engineers regularly compile Google Chrome with AddressSanitizer enabled and browse the interactive web without perceiving any UI lag or performance degradation. Such near-native speed allows safety harnesses to remain active throughout regular testing cycles rather than being reserved for isolated diagnostic runs.
- **AddressSanitizer (ASan):** Replaces legacy heap checkers by instrumenting code during generation to catch stack, heap, and global memory violations.
- **ThreadSanitizer (TSan):** Compiler-driven race detector, historically hampered in C++11 demos by early standard library support gaps.
- **`-fcatch-undefined-behavior`:** A Clang runtime check inserting negligible overhead that crashes deterministically at the site of undefined actions like invalid shifts.
- **Interactive Performance:** Running AddressSanitizer inside complex targets like Chromium incurs so little latency that interactive web browsing feels real-time.
---
[43:20](https://www.youtube.com/watch?v=NURiiQatBXA&t=2600s) C++11 Safety and the Hidden Traps of `auto` with Initializer Lists
Carruth turns his focus back to the core theme of the conference: modern C++11. While earlier speakers like Bjarne Stroustrup and Herb Sutter showcased how C++11’s memory-safe primitives, native concurrency, and lifetime semantics eliminate entire classes of errors, modern language features introduce subtle edge cases of their own. Even as standard features eliminate raw pointers and manual thread management, new traps emerge at the intersections of modern idioms.
Carruth shares an example crafted in collaboration with Clang contributor Richard Smith. A developer following modern advice decides to clean up their code by replacing an explicit type declaration with `auto`, pairing it with C++11 braced uniform initialization. While the original explicitly typed code functioned flawlessly, the refactored version with `auto` behaves pathologically and fails an assertion at runtime. Carruth humorously notes that he could not demonstrate this bug inside Clang live on stage because generalized initializer lists were not yet completely implemented in Clang's development trunk at that time.
The root cause of this failure is that deducing `auto` against a braced-init-list causes the compiler to deduce the variable’s type as `std::initializer_list<T>`. An `std::initializer_list` only provides a lightweight, temporary view over underlying storage, meaning operations that expect an independent, owned container end up accessing expired backing memory. Carruth argues that compilers must develop diagnostics for this precise trap: by inspecting how the inferred `auto` variable is utilized later in the function body, the front end can identify that binding an ephemeral `std::initializer_list` was almost certainly an accident. He argues: “It's very unlikely the user actually wanted to create a variable with the, you know, standard initializer list type. Especially when we go and look at how it's used later on. It's very, very unlikely that that was the intent of the programmer.”
- Modern C++11 constructs (smart pointers, standardized threading) resolve systemic bugs, but idiomatic combinations can produce non-obvious failure modes.
- Deduced type inference using `auto x = {... }` resolves to `std::initializer_list<T>`, frequently creating dangling references or semantic mismatches when an owned sequence container was expected.
- Clang is actively working toward static heuristics that examine downstream variable usage to warn users when `auto` inference has silently created an unwanted initializer list.
[45:36](https://www.youtube.com/watch?v=NURiiQatBXA&t=2736s) C++11 Pitfalls: `decltype`, Move Semantics, and Compiler Diagnostics
Chandler Carruth shifts focus to the next major frontier in compiler diagnostics: building warnings in Clang specifically targeted at C++11. While the standards committee worked diligently to design a safe and predictable language, real-world field experience inevitably reveals edge cases and bugs in the specification itself. Carruth argues that compiler implementers have an urgent responsibility to race ahead, identify subtle traps early, and put helpful warnings in place so newcomers are not scared away during language adoption.
```mermaid
flowchart TD
accTitle: C++11 Safety and Adoption Cycle
accDescr: Cycle showing how field experience informs standards bug fixes and compiler warnings to protect language adoption.
subgraph FeedbackLoop ["Language Evolution & Adoption"]
A["Field Experience with C++11"] --> B["Fix Standard & Language Bugs"]
A --> C["Implementer Diagnostics in Clang"]
B --> D["Safer C++11 Ecosystem"]
C --> D
D --> E["Wider Developer Adoption"]
E --> A
end
```
He highlights two immediate feature areas that harbor subtle traps:
- **`decltype`**: While developers eagerly reach for `decltype` to avoid spelling out verbose type names, certain expressions produce completely unexpected deduced types—such as unexpected rvalue references or intricate template types—leading to unpredicted behavior.
- **Move semantics**: Although intended to eliminate unnecessary copies, moving can easily introduce dangling references or accidentally move out of local variables, since references remain just references under the hood.
Carruth stresses that human code reviewers will simply not spot these subtleties on their own:
> “It's very important that as implementers of C++ 11 that we race ahead that we race ahead and find these pitfalls early and get warnings in place so that as people are learning C++ 11, as we are trying to get adoption of the language, people are not scared away by pitfalls that are easily handled and far more minor than the pitfalls of the slides you've just seen.”
---
[47:53](https://www.youtube.com/watch?v=NURiiQatBXA&t=2873s) The Ultimate Edge Case: The Accidental Null Pointer Constant
Carruth presents an obscure, unnerving edge case created by an unintended collision between three separate language mechanics: generalized constant expressions (`constexpr`), default aggregate initialization, and backward compatibility with C++98 null pointer constant rules. Showing a code snippet, he asks the audience what the program outputs versus what they *want* it to output. The audience responds that they want it to print `X`.
Under C++98, an `X` object was indeed constructed via implicit conversion. Under C++11 rules, however, the expression suddenly forms a null pointer constant and resolves to a completely different function overload. Although programmers are unlikely to write this pattern intentionally on a daily basis, an accidental encounter could cost an engineer hours of baffling debugging and deeply shake their confidence in the language.
> “This may seem like an edge case, but the time lost when it happens is massive, right? And it shakes confidence in the language. It makes people distrust C++.”
Before opening the floor to questions, Carruth makes a direct pitch for Clang contributions. Emphasizing that Clang is an open-source compiler written entirely in C++ with an inviting community, he clarifies:
> “And I don't mean patches welcome in the snarky way that a lot of people like to say like haha patch is welcome. We're not doing your work. We're not going to actually maintain this. No, no, no, no, no. I mean patches are really, really welcome.”
---
[50:38](https://www.youtube.com/watch?v=NURiiQatBXA&t=3038s) Q&A: Dissecting the Null Pointer Constant Bug
An audience member immediately asks Carruth to walk step-by-step through the perplexing C++11 constant expression example. Carruth jokes that he and Richard Smith spent a couple of days analyzing it just to convince themselves they understood it, echoing Howard Hinnant's observation that everyone is still learning the nuances of the new standard.
```mermaid
flowchart LR
accTitle: Accidental Null Pointer Constant Resolution
accDescr: Step-by-step flow showing how an aggregate default constructor produces a null pointer constant in C++11.
A["Aggregate struct S { int n; }"] --> B["Default constructor is a valid constexpr"]
B --> C["Temporary S().n zero-initializes n to 0"]
C --> D["Evaluates to integer value 0 in constant expression"]
D --> E["C++98 backward compat treats literal 0 as null pointer constant"]
E --> F["Selects pointer overload f(void*) instead of f(X)"]
```
Carruth explains the underlying mechanics:
1. An aggregate struct `S` with an integer member `n` implicitly acquires a default constructor.
2. Because `S` is a simple aggregate, that constructor qualifies as a valid constant expression that zero-initializes `n`.
3. Evaluating an expression like `S().n` forms a constant expression evaluating to the integer value `0`.
4. Because C++11 must maintain backward compatibility with C++98 rules where an integer constant expression evaluating to zero serves as a null pointer constant, the compiler cannot distinguish this zero from a literal `0` in source code.
5. Consequently, overload resolution matches a pointer parameter rather than triggering the implicit conversion to type `X`.
Another attendee asks whether this constitutes a language bug and if a Defect Report (DR) has been submitted to the ISO C++ committee. Carruth replies that Richard Smith had only discovered the behavior that past Monday, so no formal defect report had been filed yet, adding that finding a clean resolution in the wording will be tricky.
---
[53:18](https://www.youtube.com/watch?v=NURiiQatBXA&t=3198s) Q&A: Template Compile Times and Linker Overhead
An attendee asks about the performance trade-offs of sophisticated language features and heavy diagnostics, specifically whether template-heavy C++11 code will degrade compile times and link times.
Carruth shares that a colleague had run fresh compile-time benchmarks just the night before showing that Clang performs exceptionally well on heavily templated codebases:
> “Clang is about twice as fast as GCC at compiling your code. All right, and that's a big improvement. 2x is a big number in in compile time performance because people have been looking at this for a while.”
Turning to linker overhead—a notorious pain point for template instantiation bloat—Carruth agrees that link-time performance is a distinct challenge and notes that several promising proposals are actively being explored in the community to tackle link-time costs.
[54:34](https://www.youtube.com/watch?v=NURiiQatBXA&t=3274s) Modernizing Linkers and the LLVM Linker Project
Chandler Carruth reflects on linker architecture and the potential of C++ modules to shift workload ahead of time, allowing dynamic libraries to become richer artifacts that are linked once, installed on a system, and simply referenced. Beyond module-level improvements, he rejects the conventional assumption that current linkers are slow because they are tasked with too much computation. Instead, linkers remain sluggish because the industry has constrained itself by decades of backward compatibility:
> “I think that our current linkers are slow because we haven't yet fully studied the problem of how to link binaries efficiently and we haven't yet done that study in a in a radical enough mindset.”
He observes that tooling often remains tethered to file format specifications that were considered optimal in 1975, despite those designs no longer fitting modern software scale. Carruth argues that a clean-slate rethink could yield links that are orders of magnitude faster. He points to an ongoing effort inside LLVM (lld) aimed at authoring a brand-new linker, expressing optimism that radical ideas will eventually reshape binary linking.
[55:52](https://www.youtube.com/watch?v=NURiiQatBXA&t=3352s) Null Pointer Constants and Richard Smith's Clang Warning
An audience member follows up with a suggestion: while adopting `nullptr` over `0` or `NULL` would ideally resolve ambiguity, Clang could introduce a warning whenever developers write an integer zero constant that is not an explicit literal `0`—such as an arithmetic expression like `1 - 1`. Carruth explains that compiler engineer Richard Smith has already implemented precisely that warning in Clang.
Testing the warning against large codebases yielded surprising findings. While some instances exposed dubious idioms, other code sites were rife with genuine bugs. At the same time, the compiler team encountered valid C++98 codebases that deliberately declared typed integer constants (such as `const int kNull = 0;`) to avoid using preprocessor macros for null pointers. Carruth notes that although treating such constants as pointers remains dubious, it represents existing code that must be accounted for, though the warning is still expected to land in Clang.
[57:04](https://www.youtube.com/watch?v=NURiiQatBXA&t=3424s) The Hurdles Facing Clang on Windows
When asked why Clang does not yet work properly on Windows, Carruth eagerly dives into the topic. He points out that the fundamental hurdle is that Visual C++ is already a high-quality compiler, meaning the core motivators that drove developers away from legacy toolchains on Unix platforms do not strongly resonate in the Windows ecosystem. As a result, gathering momentum for the massive engineering undertaking of Windows support has proven difficult.
This lack of initial traction feeds a self-reinforcing staffing problem. The Clang community contains very few developers who possess deep Windows systems expertise, leaving the compiler ill-equipped to support the platform or review incoming patches. Carruth notes:
> “The thing we need to make Windows support and Clang a reality are two things, right? And they're the two things that every open source project I've ever in the world looked at needs: we need patches and we need infrastructure.”
Neither of the primary corporate sponsors behind Clang runs mature Windows infrastructure, nor do core maintainers possess the domain knowledge to evaluate Windows-specific contributions properly. Carruth issues an open invitation for developers with Windows expertise to contribute test infrastructure and patches to bridge the gap.
```mermaid
flowchart TD
accTitle: Obstacles to Clang Windows Support
accDescr: Diagram illustrating the feedback loop between Visual C++ quality, lack of community expertise, and infrastructure shortages on Windows.
subgraph Ecosystem["Ecosystem Reality"]
MSVC["Visual C++ is already very good"]
Demand["Less urgent developer pressure to switch"]
end
subgraph CommunityDeficit["Community Knowledge Gap"]
FewDevs["Few Clang contributors know Windows internals"]
ReviewBlock["Maintainers cannot safely review Windows patches"]
end
subgraph Needs["Requirements to Fix"]
Infra["Dedicated Windows CI & test infrastructure"]
Patches["Domain-expert community patches"]
end
[59:02](https://www.youtube.com/watch?v=NURiiQatBXA&t=3542s) Best-Practice Diagnostic Suggestions and Fix-Its
An audience member asks whether Clang plans to provide automated suggestions to guide programmers toward modern idioms—such as recommending initializer lists, standard algorithms, or `auto`. Carruth responds that offering actionable suggestions is a major goal, but outlines the strict architectural discipline Clang applies when emitting automated fix-its:
> “Clang will only actually attach a suggested fix to the diagnostic itself when the diagnostic is an error and when it can actually recover from that error by parsing as if the user wrote what it suggested.”
He explains that decades of research show automated suggestion engines easily spiral out of control, causing subsequent compiler parsing passes to run off the rails. Consequently, Clang maintains a strict bar:
- **Errors:** Fix-it hints are attached directly to the main diagnostic only when Clang can recover its internal parse state by assuming the replacement text.
- **Warnings:** Because warnings denote valid C++ code, the compiler cannot reject or recover-parse the translation unit; suggested fixes are therefore attached as supplementary `note:` diagnostics offering voluntary options to the developer.
Carruth emphasizes that as Clang rolls out diagnostics for C++11 modernization, pairing warnings with well-established best practices—such as suggesting `nullptr`—is essential for guiding the ecosystem forward safely. The session concludes with the host thanking Carruth to enthusiastic applause.
## Notable quotes
> [21:24](https://www.youtube.com/watch?v=NURiiQatBXA&t=1284s) It doesn't matter what you're storing the value into. You have to actually compute the right value first. — Chandler Carruth
> [33:05](https://www.youtube.com/watch?v=NURiiQatBXA&t=1985s) Every time we can extend the language to codify the semantics, we can actually get improved checking from our tools and especially our compilers. — Chandler Carruth
> [09:17](https://www.youtube.com/watch?v=NURiiQatBXA&t=557s) Murphy is not a passive player here. He is actively seeking out the flaws in your code. — Chandler Carruth
> [02:23](https://www.youtube.com/watch?v=NURiiQatBXA&t=143s) One of the main goals for GCC is to prevent any parts of it from being used together with non-free software. Thus, we have deliberately avoided many things that might possibly have the effect of facilitating such usage. — Richard Stallman
> [59:02](https://www.youtube.com/watch?v=NURiiQatBXA&t=3542s) Clang will only actually attach a suggested fix to the diagnostic itself when the diagnostic is an error and when it can actually recover from that error by parsing as if the user wrote what it suggested. — Chandler Carruth
## Mentioned
Chandler Carruth, Chris Lattner, Richard Stallman, Herb Sutter, Charles Torre, Andrei Alexandrescu, Richard Smith, Howard Hinnant, Bjarne Stroustrup, Clang, LLVM, GCC, Visual C++, Google, Apple, Google Chrome, Valgrind, AddressSanitizer, ThreadSanitizer, lld, ISO C++ Committee
## Video description
Chandler Carruth - Clang: Defending C++ from Murphy's Million Monkeys
Chandler Carruth argues that traditional C++ compiler designs failed modern developer scale by prioritizing ideological isolation and opaque output over semantic awareness. To overcome this, Clang was architected around reusable libraries and high-fidelity representations to defend engineers against systemic programming pitfalls.
Architectural Origins and Modular Tooling (0:00)
Clang arose from frustrations with legacy open-source infrastructure, particularly GCC. According to Carruth, GCC not only maintained an intentional policy against exposing reusable libraries to proprietary software—quoting Richard Stallman on the matter—but also employed an AST pipeline that aggressively discarded syntactic detail, such as dropping expressions like `+ 0` during early constant folding. These design decisions prevented external developer tooling, refactoring engines, and IDEs from reliably understanding source code.
In response, Chris Lattner and the LLVM community engineered Clang around modular front-end libraries and a rich, source-preserving AST. Carruth notes that by 2012, this architectural foundation delivered fully compliant C++98 support, compile times approximately twice as fast as GCC on templated codebases, and rapid velocity on the C++11 standard, with features like lambdas and `constexpr` approaching completion.
Diagnostics, Typo Recovery, and Semantic Warnings (9:17)
To counter Murphy's Law in massive engineering organizations, Clang shifted compiler output away from raw token dumps toward contextual explanation. The front end walks back nested macro expansion histories, highlights precise source ranges, and clarifies template instantiation failures—such as explicitly identifying when a template parameter resolved to 4 rather than 42 to explain a negative array size. Its semantic analysis also resolves typographical errors contextually and continues parsing under repaired assumptions to prevent cascading downstream phantom diagnostics.
Carruth highlights that warnings for valid but mistaken code serve as Clang's primary workhorse. The compiler inspects standard-compliant C++ constructs to identify evident programming bugs, such as passing a pointer into `sizeof()`, declaring a function unintentionally via an errant comma, or overflowing 32-bit signed integer math before assigning it to a 64-bit container. It also flags misleading Python-style indentation and unhandled enumerators in switch statements lacking default cases.
Static Control Flow and Concurrency Analysis (33:05)
Clang incorporates static analysis directly into compilation passes to verify runtime program behavior across complex execution paths. The compiler uses data-flow tracking to catch uninitialized variable reads and out-of-bounds container indexing at compile time, eliminating mistakes that human code reviews routinely miss.
For multithreaded systems, Clang leverages domain-specific attributes, such as `guarded_by` and `exclusive_locks_required`, to trace control-flow graphs and locksets. This compile-time thread safety analysis validates locking contracts directly against source logic, flagging unprotected reads of shared memory, inconsistent lock acquisitions across loop boundaries, and forgotten unlocks before function returns.
Compiler-Assisted Dynamic Analysis Over Binary Simulation (37:07)
Addressing invariants that static analysis cannot reliably prove across isolated translation units, Carruth contrasts compiler-assisted instrumentation with post-compilation dynamic binary tools like Valgrind. While Valgrind detects heap overruns and uninitialized reads, its full-system CPU simulation incurs severe 20x to 30x execution penalties and completely overlooks stack-allocated buffer bugs when code is refactored into cleaner local forms.
By generating safety checks during intermediate representation lowering, compiler tools like AddressSanitizer (ASan) and the `-fcatch-undefined-behavior` flag inspect both heap and stack allocations with minimal performance loss. Carruth emphasizes that ASan delivers performance near enough to native execution that developers can run complex targets like Chromium interactively without perceived lag.
Navigating Edge Cases in C++11 Adoption (45:36)
Even as modern C++11 constructs eliminate legacy bugs, Carruth observes that interacting modern features introduce new failure points that require compiler mitigations. For example, deducing `auto` against a braced initializer list yields a temporary `std::initializer_list` rather than an owned sequence container, risking silent lifetime invalidation. Other hazards involve unintended rvalue conversions via `decltype` and accidental move operations on local variables.
Carruth details an edge case where C++11 default aggregate initialization within a `constexpr` constructor evaluates to zero, which C++98 backward-compatibility rules then treat as an integer null pointer constant. This interaction silently redirects overload resolution to a pointer parameter instead of invoking an intended implicit class constructor. To protect developers adopting the new standard, Clang works to add proactive diagnostics before edge cases damage programmer trust.
While Clang has transformed C++ diagnostics and tooling velocity on Unix platforms, Carruth acknowledges that Windows support remains incomplete due to a shortage of platform-specific maintainers and testing infrastructure, and link-time bottlenecks remain an industry-wide challenge requiring clean-slate tools like LLVM's `lld`.
精炼阅读摘要
Chandler Carruth 指出,Clang 凭借模块化库架构、高保真抽象语法树(AST)与深度静态分析,旨在解决传统开源编译器诊断晦涩、编译缓慢及工具集成困难等痛点,并在大规模团队协作与复杂 C++11 特性演进中防御人为笔误与未定义行为。
架构重构与高保真语法树的起源 (0:00)
传统开源编译器(如 GCC)因早期的常量折叠机制而在前端解析阶段直接丢失源代码符号(例如丢弃 `+ 0`),并且受到 Richard Stallman 限制非自由软件集成的设计策略影响,缺乏模块化 API,导致重构与 IDE 分析工具难以共享编译器的原生语义理解。加之以往编译器在遇到花括号缺失等简单语法错误时仅输出冗长且无指导意义的词法标记转储(token dump),编译时间也极度缓慢。
针对这些结构性缺陷,LLVM 社区与 Chris Lattner 决定从零构建 Clang。Clang 采用基于库的模块化架构,核心围绕保留完整语法信息的 AST 展开,不仅实现了源码范围的高亮定位与插入符号诊断,还能够回溯多层模板实例化栈与预处理器宏展开链条。此外,Clang 引入语义恢复机制,在遇到成员初始化列表命名冲突或命名空间遗漏时,能推断出程序员的真实意图并继续解析剩余文件,避免级联错误报错,并支持直接输出代码修改建议(fix-it hints)。
语义警告拦截合法但危险的代码 (18:31)
Carruth 强调,警告机制是 Clang 诊断的核心支柱,其目标不是拦截语法违规,而是审查完全符合 C++ 标准但在语义上极大概率偏离意图的代码模式。典型的案例包括对指向结构的指针变量直接执行 `sizeof(s)`,这会导致缓冲区未完全初始化却能侥幸通过基础测试,曾潜伏在 Google Chrome 等大型项目中;其他常见隐患还包括对包含负数或越界常量的数组访问、未初始化变量的数据流读取,以及在 64 位整数赋值中由于中间运算采用 32 位有符号整数而触发的算术溢出。
在语法歧义防范上,Clang 针对逗号误替分号导致意外声明局部函数、将布尔变量误重置为裸指针(涉及 C 风格字面量 0 隐式转换)、以及在 `if` 条件中误写赋值号等问题建立了严格检查。针对条件中的合法赋值,编译器通过双重括号模式保留合法用法;面对逻辑运算符(`&&` 与 `||`)混用、流插入与算术比较运算符重叠,以及具有欺骗性的代码缩进,Clang 均通过强制显式添加括号或匹配枚举类型所有分支(无 `default` 时的 switch 语句)来消除模糊意图。
内联深度静态分析与线程安全注解 (33:05)
Clang 将静态分析直接内嵌到编译流程中,能够超越浅层语法检查并沿着控制流图(CFG)跨分支追踪状态,特别是在传统类型系统无法表达的并发编程领域。通过结合 C++11 通用属性语法(如 `guarded_by` 与 `exclusive_locks_required`),开发者可以显式标注互斥锁与受保护的数据成员契约。
编译器依据这些语义标注,全面追踪每条执行路径上的锁持有状态,即时捕获未持有锁的共享内存读取、函数退出时遗漏的锁释放、循环分支间锁状态不匹配,以及在无锁保护情况下调用受限函数的并发缺陷。Carruth 主张,通过扩展语言表达力来显式固化这些语义规则,能让编译器在代码提交前主动拦截细微的多线程漏洞。
编译级动态检测对传统模拟的替代 (37:07)
静态分析无法涵盖所有依赖复杂输入与循环计数的运行时路径,但以 Valgrind 为代表的传统二进制模拟工具存在致命局限:为了控制硬件模拟的资源开销,Valgrind 往往放弃追踪栈分配内存,导致代码重构为本地栈变量后反而无法检测到越界,且伴随 20 到 30 倍的严重性能衰减,难以用于大型测试集。
Clang 倡导在中间表示(IR)代码生成阶段直接注入轻量级运行时校验指令,由此催生了 AddressSanitizer(ASan)、ThreadSanitizer(TSan)及 `-fcatch-undefined-behavior`。这种编译器级插桩不仅能够精准覆盖栈、堆与全局变量的内存越界,并在未定义行为发生处立即触发确定性崩溃,还保持着接近原生的执行速度——工程师甚至能够流畅运行带有 ASan 插桩的 Chromium 进行实时网页浏览,使动态验证常规化成为可能。
C++11 新特性的前沿陷阱与工程边界 (45:36)
现代 C++11 特性在消除裸指针与手动生命周期管理的同时,也带来了新的边缘陷阱。例如将 `auto` 与大括号初始化混用(如 `auto x = {...}`)会导致类型被推断为轻量临时视图 `std::initializer_list<T>`,极易在使用中产生悬垂引用;此外,`decltype` 的推导规则与移动语义对局部变量的误操作同样需要编译器构建更前沿的诊断 heuristic。
针对标准演进中的缺陷,Carruth 举例展示了 C++11 常量表达式(`constexpr`)聚合初始化与 C++98 允许将值为零的整型常量表达式视作空指针常量的兼容规则发生冲突,导致重载意外匹配到 `void*`。而在跨平台与生态层面,Carruth 坦言 Clang 在 Windows 平台上的适配滞后,主因在于 Visual C++ 本身质量较高导致迁移需求不紧迫,加之社区内缺乏精通 Windows 内核的工程师与测试基础设施,仍需业界持续贡献补丁与新型链接器(lld)支持。
Clang 通过将高保真 AST、精细化诊断和编译器内嵌插桩相结合,显著重塑了 C++ 工具链的生态标准。然而,在新标准边缘语意冲突的规范化修复、对 Windows 原生环境的深度支持,以及彻底解决遗留对象格式带来的链接性能瓶颈等方面,该项目仍面临长期的工程挑战与生态协作需求。
Detailed notes — zh
## Background
本内容由 YouTube 频道 aghuttun 于 2013 年 1 月 18 日发布,记录了 GoingNative 2012 大会第二天的精彩演讲。视频页面说明显示,本场由 Google 的 Clang 负责人 Chandler Carruth 带来题为《Clang: Defending C++ from Murphy's Million Monkeys》的主题分享。
## TL;DR
Chandler Carruth 深入剖析了 Clang 诞生的核心驱动力:打破传统开源编译器(如 GCC)因政治和架构局限带来的糟糕报错体验与工具链壁垒。演讲系统阐述了 Clang 如何凭借卓越的上下文诊断、错误恢复(Fix-it)、深层宏与模板回溯,以及高级静态分析(如并发属性检查)来拦截合法但致命的逻辑缺陷。此外,他还对比了传统工具 Valgrind 与 AddressSanitizer 等编译器级动态分析的性能差异,并深入剖析了 C++11 新特性(如 constexpr、auto)落地过程中潜藏的边缘陷阱与工具链演进方向。
## Atmosphere & style
整场演讲洋溢着极具感染力的极客幽默与犀利直率的技术洞见,现场互动热烈、笑声不断。Chandler 借助英国情景喜剧般的夸张比喻,将软件缺陷人格化为主动出击的“愤怒墨菲”,生动还原了开发者面对糟糕工具链与隐蔽 Bug 时的抓狂日常。从调侃自己写出的整数溢出生产陷阱,到坦率承认 Clang 在 Windows 支持和早期规范探索中的真实困境,演讲在严谨硬核的底层语义拆解中贯穿着坦诚自嘲与对开源社区的深切热爱,观感酣畅淋漓且干货满满。
## Bulletin
- [02:56](https://www.youtube.com/watch?v=NURiiQatBXA&t=176s) Clang 的诞生源于旧有开源工具链在代码库化设计上的政治与技术阻碍;GCC 官方因理念限制拒绝将前端库化,促使 LLVM 社区独立开发全新的现代 C++ 编译器。
- [50:38](https://www.youtube.com/watch?v=NURiiQatBXA&t=3038s) Clang 在重度依赖现代模板特性的复杂 C++ 代码编译场景下,前端编译速度可达到 GCC 的两倍左右,大幅提升了构建吞吐效率。
- [37:07](https://www.youtube.com/watch?v=NURiiQatBXA&t=2227s) 传统动态分析工具 Valgrind 由于指令级模拟带来 20 至 30 倍性能开销且无法有效覆盖栈内存,Clang 推动基于代码生成期插桩的 AddressSanitizer (ASan),实现了运行大型软件如 Chrome 浏览器且交互无感知卡顿的高性能分析。
- [47:53](https://www.youtube.com/watch?v=NURiiQatBXA&t=2873s) C++11 泛化常量表达式、聚合体默认初始化与旧式空指针字面量规则的交织可能导致合法的表达式意外坍缩为整型 0,引发重载决议突变,急需编译器静态诊断筑牢防线。
## Key takeaways
- [01:16](https://www.youtube.com/watch?v=NURiiQatBXA&t=76s) 传统开源编译器存在报错信息晦涩、编译缓慢以及高度耦合等问题,无法满足现代 IDE 集成与代码重构工具的开发需求。
- [02:56](https://www.youtube.com/watch?v=NURiiQatBXA&t=176s) GCC 官方因政治与哲学考量拒绝将前端解耦为可复用库,且解析器内部的无感知常量折叠破坏了源码与 AST 的映射,直接促成了 Clang 的立项。
- [09:17](https://www.youtube.com/watch?v=NURiiQatBXA&t=557s) 软件工程中的“墨菲定律”体现为海量开发者协作敲代码时的必然失误,现代编译器的核心防线应从编译诊断与错误恢复机制切入。
- [11:49](https://www.youtube.com/watch?v=NURiiQatBXA&t=709s) 面对多层模板具现化和深层宏展开,Clang 能够完整逆向还原预处理和参数代入调用栈,精准定位生成非法代码的根源参数。
- [14:50](https://www.youtube.com/watch?v=NURiiQatBXA&t=890s) Clang 具备强大的上下文感知拼写纠错(Typo Correction)与错误恢复能力,在假定修复后继续向后解析,避免因前置小错误引发级联假报警。
- [18:31](https://www.youtube.com/watch?v=NURiiQatBXA&t=1111s) 许多引发生产事故的代码在语法上完全符合规范,编译器诊断的核心价值在于审视语义上下文、拦截“合法但绝非程序员本意”的代码模式。
- [21:24](https://www.youtube.com/watch?v=NURiiQatBXA&t=1284s) C++ 算术转换规则中 32 位有符号字面量计算溢出属于未定义行为,即便赋值目标是 64 位大整数也无法规避计算阶段的溢出错误。
- [25:44](https://www.youtube.com/watch?v=NURiiQatBXA&t=1544s) 为平衡语言表达力与错误拦截,Clang 设立安全退出机制(Escape Hatch),例如要求在有意将条件表达式写为赋值时显式包裹双层括号。
- [27:57](https://www.youtube.com/watch?v=NURiiQatBXA&t=1677s) 由于人类直觉易受布尔代数优先级与代码缩进欺骗,Clang 针对未加括号的逻辑运算符混用、位运算与算术运算混淆以及未穷举的 enum switch 设立了强制警告。
- [33:03](https://www.youtube.com/watch?v=NURiiQatBXA&t=1983s) 借助 C++11 泛化属性标注(如 guarded_by 和 exclusive_locks_required),Clang 能够在编译期对复杂多线程代码的控制流进行深度并发静态分析。
- [40:41](https://www.youtube.com/watch?v=NURiiQatBXA&t=2441s) 由编译器在代码生成期辅助插桩的动态分析技术(如 ASan 和 TSan)兼顾了极高的执行效率与全面的内存覆盖面,打破了传统模拟器工具的性能天花板。
- [45:36](https://www.youtube.com/watch?v=NURiiQatBXA&t=2736s) C++11 新特性虽然提升了语言安全性,但 auto 配合列表初始化、decltype 意外推导以及右值过度移动仍会引入隐晦陷阱,需要编译器走在前面设立预警。
- [54:34](https://www.youtube.com/watch?v=NURiiQatBXA&t=3274s) 传统链接器受限于数十年前制定的文件格式与兼容包袱,未来需借助 C++ Modules 提前分担工作并以激进设计(如 lld)重构底层链接效率。
- [57:04](https://www.youtube.com/watch?v=NURiiQatBXA&t=3424s) Clang 早期缺乏 Windows 良好支持的主因是 Visual C++ 本身足够优秀导致动力不足,同时社区在底层人才和构建基础设施上严重匮乏。
- [59:02](https://www.youtube.com/watch?v=NURiiQatBXA&t=3542s) Clang 的 Fix-it 机制遵循严格边界:错误诊断仅在能够进行可靠的“恢复性解析”时提供修复建议,而警告诊断的建议则全部作为自选 Notes 呈现。
## Detailed walkthrough
### [00:00](https://www.youtube.com/watch?v=NURiiQatBXA&t=0s) 开场介绍与现场互动
主持人首先向观众介绍了演讲嘉宾 Chandler Carruth,称赞他是“年轻的新一代开发者的代表”。作为 Google 的 Clang 负责人,Chandler 走上讲台为大会分享 Clang 的相关进展与实践。Chandler 首先向会议组织者 Herb Sutter 和 Charles Torre 表达了感谢,认为这是一个难得的机会,可以面向许多在日常 C++ 开发中尚未接触过 Clang 的开发者进行宣讲。
随后 Chandler 进行了现场互动,调查台下听众的使用情况:他询问有多少人哪怕只用过一次 Clang,随后又询问有谁在上个月内使用过,甚至调侃地问是否有观众是因为知道有这场演讲才临时去试用 Clang 的。面对台下的反应,他幽默地指出“有些人可不够诚实”。Chandler 解释道,Clang 作为一个较新的项目,虽然目前支持的平台相对有限,但正处于迅速普及和发展的阶段。
### [01:16](https://www.youtube.com/watch?v=NURiiQatBXA&t=76s) 为什么我们需要另一个 C++ 编译器?
面对“市面上已有不少 C++ 编译器,为何还要重写一个”的疑问,Chandler 直言构建一个 C++ 编译器绝非易事。但要理解为什么需要 Clang,就必须先看看当时开源社区开发者所不得不忍受的糟糕工具链。相比于体验较为友好的微软 Visual C++,当时的开源编译器表现令人沮丧。
开发者日常写代码时难免疏漏,比如漏掉几个大括号或是在错误的位置加了符号。然而旧有的开源编译器报错时只会机械地吐出一长串没有任何语义价值的 token 列表,这对排错毫无帮助。除了糟糕的错误提示,这些编译器在编译速度上也极其缓慢,严重浪费开发者的时间。但更致命的核心痛点在于:开发者需要的不单单是一个编译器,而是一整套围绕语言构建的周边工具(IDE 集成、代码分析等),而任何工具的开发都不应该从零重新实现一遍对 C++ 语法的理解。
```mermaid
flowchart TD
accTitle: 开源 C++ 编译器的历史痛点与诉求
accDescr: 对比传统开源编译器的缺陷与现代 C++ 工具链的真实诉求
subgraph Legacy["传统开源编译器的困局"]
L1["无用的报错信息<br/>(仅堆砌 Token)"]
L2["极其缓慢的编译速度"]
L3["代码高度耦合<br/>无法复用为开发库"]
end
subgraph Needs["现代开发者的实际需求"]
N1["清晰可读的诊断提示"]
N2["极高的编译与分析性能"]
N3["模块化库<br/>驱动周边工具与 IDE"]
end
Legacy -.->|"催生诉求"| Needs
```
### [02:56](https://www.youtube.com/watch?v=NURiiQatBXA&t=176s) GCC 的政治阻碍与技术短板
当有人提议将 GCC 的前端做成可复用的库(Library)以便构建现代开发者工具时,却遭到了来自 GCC 官方的直接拒绝。Chandler 引用了 Richard Stallman 在 GCC 邮件列表中的原话:
> “One of the main goals for GCC is to prevent any parts of it from being used together with non-free software. Thus, we have deliberately avoided many things that might possibly have the effect of facilitating such usage.”(GCC 的主要目标之一就是防止其任何部分与非自由软件一起使用。因此,我们故意避免了许多可能有助于这种用途的事情。)
Chandler 尖锐地评价道:“This is not a useful answer. This is a political answer. This is a social answer and is not a useful answer and we cannot build our tools with this.”(这不是一个有用的答案。这是一个政治性的答案,一个社会学维度的答案,绝非有用的技术答案,我们根本无法在此基础上构建我们的工具。)
除了理念上的抵制,GCC 在技术架构层面也存在严重硬伤。当团队试图使用 GCC 分析一段写得并不高明的简单 C 语言源码(例如包含 `+ 0` 这类无意义操作的代码)时,GCC 的解析器在词法/语法阶段就直接执行了平凡常量折叠(trivial constant folding),把 `+ 0` 这类 token 直接丢弃,随后反馈出来的抽象结构与开发者输入的原始源码根本不一致。没有精确的源码语义模型,任何重构、静态分析等微小工具的开发在 GCC 上都寸步难行,甚至会引发工具对代码的误改。
- **GCC 的技术限制**:在 Parser 内部直接进行无感知常量折叠,破坏了源代码与语义分析模型的映射映射关系。
- **工具链诉求落空**:开发者无法基于 GCC 获取准确的源码位置信息与语法树结构,难以构建代码补全、重构和格式化工具。
### [05:32](https://www.youtube.com/watch?v=NURiiQatBXA&t=332s) LLVM 社区的崛起与 Clang 的诞生
面对这一行业僵局,Chris Lattner 在苹果公司主导的 LLVM 社区站了出来。秉持着真正的开源探索精神,Chris Lattner 提出了直接而有力的解决方案:“I know how to solve this problem. I'll write a new one.”(我知道怎么解决这个问题,我来重新写一个编译器。)于是,Clang 应运而生。它的诞生完全源于无法在旧工具链上支持真正 C++ 开发的深重挫败感与现实需求。
随后,Chandler 介绍了 Clang 在当时的技术现状与未来规划:
- **C++98 支持现状**:Clang 已经是一个成熟完备的 C++98 编译器,在 Linux 和 Mac OS X 上运行极佳,整体代码 bug 极少且库化设计完善(Windows 平台的支持仍在追赶中)。
- **C++11 支持进展**:Clang 对新标准 C++11 的跟进速度远超外界预期。在当时缺失的特性极少,仅剩初始值列表(initializer lists)、常量表达式(constexpr)的最后部分、Lambda 表达式、无限制联合体(unrestricted unions)以及继承构造函数和原子操作支持。
- **下一版本预期**:团队预计在接下来的版本中补齐 constexpr、initializer lists 和 lambdas,使 Clang 紧跟语言前沿规范。
在梳理完 Clang 的起源与技术现状后,Chandler 转向了本场演讲的核心主题——在实际工程中对抗“墨菲定律”。他指出,在面对庞大团队和 C++ 这门复杂的语言时,错误必将发生;而演讲的其余部分将深度剖析如何通过编译器的设计来防范程序代码中可能出现的各种极端疏漏与灾难。
### [09:17](https://www.youtube.com/watch?v=NURiiQatBXA&t=557s) 抵御“愤怒的墨菲”与庞大的开发者群体
Chandler Carruth 指出,如果缺乏完备的安全防护机制,软件开发很容易滑向灾难。在整个软件构建过程中,“墨菲”(墨菲定律的人格化象征)绝不是一个旁观者,而是一个极其主动的对手。“Murphy is not a passive player here. He is actively seeking out the flaws in your code. Murphy is an angry angry dude.” Chandler 借用一部英国电视剧里愤怒角色的形象来形容这种威胁:只要稍有疏忽,墨菲就会冷不防地找上门来,随之而来的绝非轻微小失误,而是破坏力极强的大灾难。
然而,C++ 本身其实是一门非常安全的语言。如果全天下只有一个程序员在与墨菲斗智斗勇,防御工作尚可应付;真正的挑战在于现实中有着成千上万名开发者——如同无数只在键盘上疯狂敲击的“代码猴子”(code monkeys)。当团队规模急剧扩张、无数程序员协作寻找正确的 C++ 代码时,出错的概率呈指数级攀升。Chandler 特别澄清,他所谓的“灾难”不一定是指战斗机坠毁或海上石油钻井平台爆炸这种物理层面的事故;错过发布日期、开发者生产力崩盘、甚至因代码晦涩难懂而导致的集体受阻,在长期与全局视角下同样是毁灭性的灾难。为了帮开发者分担负担,Clang 团队决定回归本质,从最基础、最频繁打交道的地方入手:编译诊断(Diagnostics)。
- 墨菲代表了代码中必然会被触发的最坏情况与主动漏洞搜索力量。
- “灾难”的定义不仅包括重大的物理/工程安全事故,也包括延误产品发布期(ship date)以及开发者生产力严重受损。
- 解决海量开发者协作敲代码(herd of monkeys)带来的隐患,切入点是编译器的基础诊断能力。
---
### [11:49](https://www.youtube.com/watch?v=NURiiQatBXA&t=709s) 模板实例化与宏展开的深度诊断追踪
在展示了一段声明了负数大小数组的错误 C++ 代码后,Chandler 详细拆解了 Clang 诊断信息的精妙层级。面对编译错误,许多人常常在匆忙赶工时错过问题的本质。Clang 不仅指出程序非法(如声明了负大小数组),用下划线和高亮精确标出错误位置,还会沿着调用栈一路回溯。
```mermaid
flowchart TD
accTitle: Clang Diagnostic Unwinding Flow
accDescr: Visualizing how Clang traces errors through template instantiations or macro expansions back to the original user code.
subgraph MacroOrTemplate["实例化 / 展开源头"]
A["用户调用端 (如宏 M2 或模板调用)"]
end
subgraph ExpansionLayers["诊断回溯栈 (Unwinding Stack)"]
B["中间展开层 (参数代入、内层宏/模板)"]
C["底层具现化 (具体参数代入,如 N = 4)"]
end
subgraph FinalError["精确定位错误"]
D["非法代码生成 (如声明负大小数组)"]
end
A --> B
B --> C
C --> D
```
针对模板,Clang 会主动说明当前错误是在哪个类的具现化(instantiation)中发生,并把具体的模板参数打印出来(例如打印出 `n = 4`,开发者便能瞬间意识到自己写成了 4 而不是 42)。更进一步,面对比模板更令人头疼的宏(macros),Clang 展现了同样强大的上下文还原能力。即便许多人赞同 Herb Sutter 倡导的“告别宏的未来世界”,现实中我们仍不得不依赖充斥着宏的古老库。Clang 能够把预处理器的每一层文本展开轨迹完整逆向展现,告诉开发者哪个实参被传进了下一层展开,并在最终代码里指明其落脚点。“Clang is walking back through each layer of the macro expansion, showing you what the next step is and exactly where in that next step that erroneous piece of code lived.” 这种回溯极大减轻了排查第三方宏黑盒代码时的痛苦。
- 诊断首要展示:确切的错误原因(如负大小数组)及代码下划线高亮。
- 具现化调用栈(Instantiation stack):显示完整的模板参数代入值(如具现化中明确给出 `n = 4`)。
- 宏层级回溯:文本预处理阶段的层层展开均有上下文溯源,准确定位是哪一个中间参数引发了错误代码的生成。
---
### [14:50](https://www.youtube.com/watch?v=NURiiQatBXA&t=890s) 智能拼写纠错、命名空间推导与错误恢复机制
针对程序员在极速打字时最容易犯的低级错误——拼写笔误(typos),Chandler 展现了 Clang 令人惊叹的语义理解与智能修复能力。在一则示例中,代码里同时存在两个名为“base”的实体:一个是静态变量,一个是类型。如果程序员在成员初始化列表中误写了名字,Clang 不会机械地抱怨符号未解析,而是基于语义分析判断出:静态变量根本不可能合法出现在成员初始化列表中,因此开发者的真实意图必定是那个类型。
更为关键的是 Clang 卓越的“错误恢复”(Error Recovery)哲学。在指出拼写错误后,Clang 不会放弃编译或产生连锁乱报,而是假设开发者写的就是正确的类型并继续解析整篇源文件,确保后续报错依然具备高精准度。当遇到遗漏命名空间限定符以及连环拼写错误(例如命名空间未限定且函数名还敲错为 `fooar`)时,Clang 会先提示缺少限定符,在假定该修复成立的基础上顺藤摸瓜纠正后续的拼写问题。“This changes how you read diagnostics. This actually allows you to not send all of your error messages into head and just look at the first line because you're going to continue to get accurate and useful error messages throughout the file.” Chandler 强调,以往程序员被逼得只能把编译器报错通过管道传给 `head -n 1` 只看第一行,而 Clang 的错误恢复能力彻底改变了这种习惯。如果开发者需要,Clang 甚至有能力按需自动重写修正源码。
- 语义感知纠错:在同名变量与类型并存时,依据上下文语法规则(如构造函数初始化列表)自动筛选出合理候选项。
- 连续错误恢复(Recovery):修正前一个假定错误后平滑继续向后解析,避免因前置小错误引发成百上千行的“级联假警报”。
- 自动修复能力:支持输出 fix-it 补丁,甚至在得到指令后直接编辑源码消除 typo。
---
### [17:19](https://www.youtube.com/watch?v=NURiiQatBXA&t=1039s) 契合语境的代码语义诊断与参数包展开报错
Chandler 进一步将话题切入到更具迷惑性的场景:代码在语法上完全符合规范(Well-formed / Valid),在编译器看来挑不出格式毛病,但运行时逻辑完全与预期背道而驰。这类问题往往是由古老的、缺乏类型安全保护的陈旧 C 接口(crusty C interface)导致的,开发者很容易在这种接口上掉入陷阱。
在翻阅幻灯片时,Chandler 补充展示了因前一场 Andrei Alexandrescu 的演讲而临时加入的一项特性——被称为“奥斯卡·王尔德(Oscar Wilde)诊断”的变长模板展开场景。当程序员在 C++11 中对两个模板参数包(parameter packs)进行同步解包展开,而两者的参数数量并不相等时,传统的编译器往往会抛出铺满数个屏幕、犹如天书般的冗长报错。而在 Clang 中,编译器直接精准击中要害,干净利落地指出参数包无法匹配的原因:“because you had two instead of three”(因为一个传了 2 个参数,而另一个需要 3 个)。这种化繁为简的直观反馈,展示了现代化诊断在驯服复杂语言特性时的巨大力量。
- 语法合法但逻辑错误:老旧 C 风格接口极易诱发不易察觉的笔误与参数传递逻辑缺陷。
- 变长参数包对齐(Parameter Pack Expansions):面对多包同步展开长度失配的复杂模板元编程错误,Clang 摈弃了冗余的实例化垃圾信息。
- 直指核心指标:以极简明了的“2 与 3 数量不匹配”反馈,取代以往动辄数屏的模板错误堆栈。
### [18:31](https://www.youtube.com/watch?v=NURiiQatBXA&t=1111s) 诊断信息的看家本领:从“合法但错误”的代码中拯救程序
编译器诊断远不止报错那么简单。对于 Clang 而言,警告信息才是诊断系统的立身之本。Chandler Carruth 强调了一个至关重要的概念:很多引发严重缺陷的代码并不是“语法无效的代码(invalid code)”,C++ 标准完全允许写出这些代码,并且对其行为有白纸黑字的明确定义。然而,编译器通过审视上下文可以完全断定:程序员绝对不想这么写。
以一个常见的 C 风格错误为例,程序员在 `sizeof` 中错误地传入了指针变量名 `s`,试图去计算指针所指向结构体的大小,结果只取到了指针本身的大小。Chandler 指出,Clang 凭借对代码上下文的语义理解,不仅能直接指出 `sizeof` 括号内使用了错误的表达式,还能给出修复建议。这类警告在真实的开源浏览器 Chrome 以及各类实际项目中捕获了无数 Bug。这种错误之所以极其可怕,是因为它在运行时“大多数时候还能正常工作”——它成功初始化了一部分内存数据,仅仅漏掉了剩下的部分,导致故障排查异常艰难。
编译器进一步把这种语义分析拓展到更多日常编码场景中:
- **无用的表达式与遗漏的返回**:程序员写出了一句毫无作用的表达式,甚至函数末尾都漏掉了 `return`。虽然语法完全符合 C++ 规定,但语义分析清楚地表明这里出了纰漏。
- **静态数组越界**:例如在明确只有 100 个元素的数组中直接访问索引 `100`,编译器会提醒该处已越界,合法的最大下标应该是 `99`。
- **未初始化变量分析**:如果代码中存在完全未给变量 `x` 初始化的控制流分支,人类肉眼审查容易忽略,但编译器可以轻松完成证明,并指出变量在哪里被未初始化使用,甚至指导用户如果确实不在乎该如何让编译器闭嘴。
> Chandler Carruth 强调编译器介入的必要性:“This is not invalid code. It's an important distinction. The C++ standard allows you to write this code and it says exactly what it will do. But we can look at the code. We can look at the context of the code and we can be completely certain that you did not mean to write it.”
---
### [21:24](https://www.youtube.com/watch?v=NURiiQatBXA&t=1284s) 整数溢出与未定义行为:连演讲者自己都踩过的真实生产陷阱
Chandler 分享了一个他本人编写并亲身踩坑的真实案例。这段代码当年不仅骗过了他自己,还骗过了数位共同参与 Review 的资深工程师——大家都觉得代码毫无破绽。代码中谨慎地挑选了一个容量极大、具备良好移植性的 64 位整数类型,试图为 8GB 空间计算字节数。
然而,C++ 的整数算术转换规则极其反直觉。由于参与运算的字面量本身是有符号的 32 位整数,计算在发生提升或存入大整数类型变量之前,就已经在 32 位有符号运算中发生了溢出。在 C++ 中,有符号整数溢出属于未定义行为(Undefined Behavior),Chandler 开玩笑说这“甚至能让计算机去给你订一份墨西哥卷饼”,因为从标准角度看它已失去任何保证。Clang 在处理此类问题时,会精准拆解整个表达式:指出程序员试图构造的值有多大、运算实际需要的位数、当前运算实际拥有的位数,以及溢出到底发生在哪个阶段。这警示所有开发者:不管目标变量有多大,计算本身的子表达式必须先算出正确的结果。
```mermaid
flowchart LR
accTitle: C++ 整数溢出与赋值陷阱
accDescr: 展示字面量默认按32位进行有符号运算导致溢出未定义行为,随后才存入64位变量的过程。
subgraph Evaluation ["字面量运算阶段 (32-bit Signed)"]
A["整数算术表达式"] -->|"32位计算溢出"| B["发生未定义行为 (Undefined Behavior)"]
end
subgraph Assignment ["存储阶段 (64-bit)"]
B -->|"错误/截断结果"| C["存入 64 位宽整数变量"]
end
```
> Chandler Carruth 针对类型陷阱总结道:“It doesn't matter what you're storing the value into. You have to actually compute the right value first.”
---
### [23:02](https://www.youtube.com/watch?v=NURiiQatBXA&t=1382s) 最隐秘的打字错误与指针语义灾难
在展示的代码片段中,Chandler 呈现了一个他职业生涯见过的“最优雅的笔误”。整段代码排版漂亮、极具欺骗性,在生产代码库中潜伏了整整好几年,经过多轮排查都无人察觉异样。现场许多观众一眼也未能看出破绽。
这个 Bug 的根源在于本应写分号(`;`)的地方被误打成了一个逗号(`,`)。导致的结果是:紧随其后的调用语句根本没有触发函数调用,而是变成了一个奇怪的本地函数声明(Local Function Declaration)。Clang 不仅能分析出这里错误地声明了一个函数,还会去检查当前作用域内是否存在同名的可调用函数;一旦确认确实存在同名可调用函数,Clang 就会推断出程序员的真实意图,明确提示:“此处原本应是分号而非逗号”。极少有工程师仅靠肉眼阅读就能明白为什么该文件不仅编译通过、运行中却从未真正调用目标函数。
紧接着是另一个关于状态更新的生产事故案例。系统试图更新客户状态,以此决定是否需要向客户扣款收费。Chandler 打趣道,收费搞错就跟“放火烧机房”一样是商业灾难。然而代码未能如愿修改布尔标记,而是因为古老松散的指针隐式转换规则,将一个指针置为了 `nullptr`。现场很多工程师看代码时甚至质问“这代码里哪来的 null?”。这一惨痛教训正印证了现代 C++ 引入显式 `nullptr` 并逐步淘汰原始裸指针隐式行为的重大意义。
- 逗号误用导致语句从“函数调用”沦为“局部函数声明”,让实际业务逻辑完全静默失效。
- Clang 深入比对符号表,逆向推导代码原本意图并给出修补分号的建议。
- 隐式类型转换使得意图设置状态的赋值变成了给指针赋空值,凸显了拥抱现代 C++ 类型安全特性的必要性。
> Chandler Carruth 在剖析此案例时感叹道:“There are very few programmers who are going to look at this and understand why this compiles cleanly and yet does not call bar.”
---
### [25:44](https://www.youtube.com/watch?v=NURiiQatBXA&t=1544s) 条件赋值警告与安全退出机制(Escape Hatch)
在条件判断中把相等比较(`==`)误写成赋值(`=`),堪称 C/C++ 历史上最著名的笔误之一。Chandler 提到,任何一个教授 C++ 的课堂上,开学头一个月内至少有四分之一的学生会犯这个错误。现代编译器(包括 Clang 和 GCC)在此类问题上形成了高度默契,联手围剿此类模式。
然而这也引发了一个深刻的语言设计哲学问题:如果程序员在条件表达式中**真的**故意想要执行赋值操作,编译器该如何处理?编译器绝不能剥夺语言本身的表达力,否则会伤害语言的灵活性。解决之道是引入一种显式的源码规约:
1. **确定性错误模式(Certain Bug Patterns)**:编译器百分之百断定用户写错了,直接报告警告并要求修正。
2. **易出错模式(Bug-prone Patterns)**:代码虽然危险,但存在合法使用意图。此时编译器提供“安全退出通道(Escape Hatch)”。
在条件赋值的情形下,只要程序员在赋值表达式外层额外包裹一层括号(如 `if ((x = get_val()))`),编译器就会理解这是程序员经过深思熟虑后的刻意行为,从而保持沉默。这种利用无害语法改造来压制警告的方式,既保全了语言能力,又筑牢了安全防线。不过,Chandler 在末尾抛出了新的戏剧性转折:当大家都习惯了用多重括号来安全使用条件赋值后,人们又开始犯下相反的错误——在本来想写比较的地方套上了双层括号,带来了意想不到的新挑战。
> Chandler Carruth 阐释语言表达力与警告压制之间的平衡:“What do you do when the compiler is warning you about code and you actually meant it. You you actually needed to do exactly what you wrote. And we can't take that expressivity of the language away. That would harm the language. That would harm programs. Instead, we add extra syntax to make it explicit to the compiler that this was intentional.”
### [27:57](https://www.youtube.com/watch?v=NURiiQatBXA&t=1677s) 运算符优先级与逻辑陷阱的静态拦截
Chandler Carruth 解释道,针对条件表达式中赋值语句容易与相等性测试混淆的问题,Clang 确立了严格规范:若开发者确实需要将赋值表达式作为条件,必须显式添加括号加以确认,否则编译器将报错制止。然而,需要重构以消除歧义的场景远不止于此。最令 Chandler 以及许多资深 C++ 工程师感到意外的,是逻辑运算符之间隐蔽且极易引发 Bug 的优先级机制。布尔逻辑本身遵循严格的代数规则,其优先级由此推导而来,但人类直觉往往脱离这种代数思维,导致写出的条件关系与真实语义完全相悖。
```mermaid
flowchart TD
accTitle: Clang 逻辑与位运算优先级警告机制
accDescr: 展示 Clang 如何对隐式优先级的逻辑表达式及算术运算符混用提出拦截并要求显式括号
subgraph LogicOps["逻辑运算符 (Logical Ops)"]
A["'a && b || c' 隐式优先级混合"] --> B{"Clang 强制检查"}
B -->|"警告并要求明确分组"| C["显式括号: '(a && b) || c'"]
end
subgraph BitShiftOps["位运算与算术运算 (Shift & Arithmetic)"]
D["表达式: x + (flag << 1) 混用加法"] --> E{"意外优先结合加法"}
E -->|"永远恒真或恒假"| F["逻辑短路/固定返回值错误"]
end
```
为了彻底封死此类漏洞,Clang 引入了强制性诊断规则:只要 `&&` 与 `||` 之间存在未用括号澄清的隐式优先级组合,编译器就会抛出警告,要求开发者显式加上括号进行逻辑分组。编译器不仅清晰解释潜在的认知陷阱,还会直接提供最符合预期的修复建议。
紧接着,Chandler 举出了一个在工程实践中更加令人啼笑皆非的算术与位运算优先级案例。他向台下观众提问某个函数的返回值究竟是什么,台下推测后 Chandler 揭晓答案:“X always X never anything other than X.”(永远是 X,绝不会是其他任何值)。这一现象令无数工程师深感震惊:加法运算符(`+`)的优先级竟然先于位移或流输出运算符发挥作用;加上 C++ 内部广泛存在的向 `bool` 的隐式类型转换,极易导致一连串级联计算错误。这种现象在使用 `std::cout` 或 `std::cerr` 的流输出操作符(`<<`)时格外猖獗,一旦两侧混杂整数与布尔判断,表达式语义往往走向完全不可控的歧义状态。
---
### [30:19](https://www.youtube.com/watch?v=NURiiQatBXA&t=1819s) 缩进欺骗、枚举缺失与常量越界
随后 Chandler 展示了一个颇具讽刺意味的代码段:由于排版缩进极其规整,肉眼极易被其伪装出的逻辑层次所蒙蔽。他调侃道:“Like like the seductiveness of Python's indentation just knows no bounds, right?”(就像 Python 那种缩进的诱惑力简直毫无节制,对吧?)。尽管视觉上代码看似完美,Clang 却能完全无视表象缩进,直接洞察语法树并发出致命警告,指明它运行起来根本不是作者所想的那样。
在代码维护方面,针对 `enum` 的 `switch` 语句是另一大高发事故区。Chandler 指出,当一个被广泛引用的枚举类型新添了一个枚举值(例如示例中遗漏的 `Z`)时,维护者根本无从获知全工程究竟有多少处 `switch` 遗漏了对该分支的处理。Clang 为此建立了极其系统化的防线:除非开发者显式提供了 `default` 兜底分支,否则编译器强制要求枚举项必须全部罗列无遗,一旦遗漏立即报警。
在分析一段常量保护逻辑时,台下观众敏锐指出了示例中同时潜藏的另一个缺陷,Chandler 笑着接梗调侃:“Every single example of code I have is buggy in more than one way, apparently.”(显然,我拿出来的每一个代码示例都坏了不止一处)。在这类场景中,代码中常常利用全局常量或预处理器标记来进行构建配置与特性开关,避免过多的宏技巧。然而这些条件分支内部往往残留着针对非法越界状态的死代码(如固定下标 44 超出静态数组容量)。Clang 的静态语义分析能紧密追踪这些常量语义,在编译期间直接捕捉深层次的逻辑越界。
---
### [33:03](https://www.youtube.com/watch?v=NURiiQatBXA&t=1983s) 借助 C++11 特性注解的深度并发静态分析
Chandler 郑重强调,编译器警告的本质已经演化为深度的程序行为静态分析:“We're doing full-blown static analysis during your compilation to give you diagnostics about what is wrong with the behavior of your program.”(我们正在编译期间进行全套的静态分析,以便针对你程序行为中的错误给出诊断)。面对大型工程中结构盘根错节的复杂例程,诸如多线程并发访问、全局共享状态和资源互斥等问题,单凭人工 Code Review 极难发现前置约束(Preconditions)的破坏。
```mermaid
flowchart TD
accTitle: 编译期多线程属性标注与约束检查
accDescr: 说明 Clang 如何利用 C++11 属性注解对互斥锁与临界区数据进行全面合规性审计
subgraph Annotations["C++11 属性标注 (Language Attributes)"]
G["共享变量声明"] -->|"[[guarded_by(mu)]]"| LockRule["绑定互斥锁 mu"]
H["函数调用契约"] -->|"[[exclusive_locks_required(mu)]]"| CallRule["必须持有排他锁"]
end
subgraph Verification["Clang 编译期静态分析 (Static Analysis)"]
LockRule --> Check["控制流路径审计"]
CallRule --> Check
Check -->|"未持锁读取数据"| Err1["Diagnostic: 读取受保护变量报错"]
Check -->|"循环延续跳过上锁"| Err2["Diagnostic: 循环回跳未加锁报错"]
Check -->|"函数退出未释放锁"| Err3["Diagnostic: 退出路径析构未释放锁"]
end
```
常规编译器无法获知人类开发者意图中的并发约束。要解决这一痛点,唯一的途径就是拓展语言层面的元数据表达,利用 C++11 泛化属性(Generalized Attributes)为源码附加上强类型的语义契约:
- **`[[guarded_by(mutex)]]`**:声明特定共享变量的读写必须由给定的互斥锁保护。
- **`[[exclusive_locks_required(mutex)]]`**:约束调用某关键函数或临界区前必须持有的排他锁。
Chandler 坦承,在当时的 Clang 中,C++11 标准双中括号属性语法(`[[...]]`)尚处于开发半程阶段,眼下演示仍借由带下划线的旧式 GNU 属性(`__attribute__`)来运作,但他强调未来的标准格式绝不会再有冗杂难看的下划线。通过引入这一契约体系,Clang 在静态分析阶段对函数内的每一条控制流分支实施详尽审查:
- 成功捕获了在未持有互斥锁的情况下越权读取被保护变量的行为;
- 成功拦截了调用必须持锁的目标函数时存在的脱锁调用;
- 标出了在循环控制流回跳(Loop Continuation)时丢失重新加锁的严重漏洞;
- 指出了函数在多路径退出时未能正确解锁的资源泄漏隐患。
Chandler 总结道,这种强大的静态安全网并非针对普通的平庸代码,而是专为那些关乎系统命脉、极其复杂微妙的多线程并发逻辑保驾护航。工具链能力的跃升与语言演进相辅相成:“Every time we can extend the language to codify the semantics, we can actually get improved checking from our tools and especially our compilers.”(每当我们能够扩展语言来规范和表达这些语义,我们就能从工具、特别是编译器中获得大幅提升的检查能力)。
### [037:07] 动态分析的必要性与传统工具 Valgrind 的局限
静态分析非常强大,能够设定关于程序的不变量(Invariants),并在所有可能的执行路径上严格执行这些不变量,无论实际运行走哪条分支。然而,还有另一种分析关注的是那些无法在整个代码库中死板强求、但本身又极其重要的不变量,这正是动态分析的用武之地。例如在一段写得很糟糕的代码中埋藏了两个 Bug:其中一个可以通过静态分析轻松找出;但另一个则非常棘手,不仅需要计算循环迭代次数(Loop trip count),而且只要对代码稍作微调或重排,静态分析就完全无能为力了。此时,唯一可行的途径就是深入程序的实际执行过程,检查其不变量与基本正确性是否被破坏。
在不涉及 Clang 的传统世界里,Valgrind 是做这类动态检查的经典工具。它通过在运行时对程序执行进行插桩(Instrumentation),能够精准地报告未初始化变量的使用以及越界读取数组等严重问题。但 Valgrind 存在极其深刻的底层缺陷:
```mermaid
flowchart TD
accTitle: Valgrind vs. Compiler-based Instrumentation
accDescr: 比较 Valgrind 动态模拟器与 Clang 编译期插桩在覆盖面与性能上的核心差异
subgraph ValgrindApproach["Valgrind 方式"]
V1["可执行二进制文件"] --> V2["指令级模拟运行 (20x-30x 性能开销)"]
V2 --> V3["为了可用性放弃栈内存检查"]
V3 --> V4["简化代码反而漏报 Bug"]
end
subgraph ClangApproach["Clang 编译器辅助方式"]
C1["C++ 源代码"] --> C2["代码生成期直接插入轻量检查"]
C2 --> C3["全量覆盖: 堆、栈、全局变量"]
C3 --> C4["几乎无感的高性能 (AddressSanitizer)"]
end
```
Valgrind 无法插桩所有行为。如果将原本的代码稍微简化改写,代码复杂度虽然降低了,但用 Valgrind 运行时抓出来的 Bug 反而变少了。Chandler Carruth 直言这种现象非常荒唐:
> “This is a very big problem because usually we're telling people to simplify their code, right? You don't you don't add complexity to your code to catch bugs. You simplify it to catch bugs. But as we simplify our code, Valgrind ceases to be able to analyze it effectively.”
之所以会出现这种矛盾,是因为 Valgrind 本质上是在机器码层面动态插桩并模拟整个程序的执行,开销极其高昂。为了让运行速度在现实中勉强可用,它不得不做出妥协,直接跳过对某些预期极快的行为的检查——比如栈(Stack)上的内存操作。结果就是栈上的越界直接被漏掉了。然而 C++ 的核心魅力恰恰在于能够同时写出既快又简单的代码,开发者理所当然会把变量放在栈上,分析工具理应诊断出这类错误,可 Valgrind 根本做不到。更糟糕的是,即便做出了这些妥协,Valgrind 依然会带来 20 倍甚至 30 倍的运行性能下降(20x-30x slowdown),庞大的程序常常因为过慢而无法跑完测试流程,根本抓不到 Bug。
---
### [040:41] 编译器辅助分析:AddressSanitizer 与未定义行为检测
要从根本上突破这个瓶颈,出路在于让 Clang 自身来承担动态插桩。Clang 不单单是一个前端,它不仅分析代码,还负责生成代码(Code generation)。在代码生成阶段,Clang 能够精确生成运行时验证各项不变量所需的轻量级检查代码,从而实现高得多的执行效率与检查精度。这正是 LLVM/Clang 持续演进的核心领域:用于替代部分 Valgrind 功能的 AddressSanitizer(ASan),以及紧随其后用于检测数据竞争(Data race detection)的 ThreadSanitizer(TSan)。
现场 Chandler 坦言原本准备了 ThreadSanitizer 的演示,但遗憾的是目前支持 TSan 的库都还不支持 C++11 原生线程,而他又绝不可能在演讲中现场手写 POSIX 线程(pthread):
> “...all of the libraries that work with thread sanitizer don't support threads in C++ 11 and I was not going to write pthreads for this talk. That that would just be too I couldn't I couldn't get up here and actually project pthreads at you guys like I would get run off the stage rightfully so.”
不过在当前的 Clang 中,开发者已经可以使用针对未定义行为的动态检测机制。例如在函数间跨文件调用、或者调用第三方供应商提供的库时,静态分析根本无法跨越边界捕捉到某个过量移位(Shift too far)的 Bug。对于这种情况,可以使用 Clang 的 `-fcatch-undefined-behavior`(UndefinedBehaviorSanitizer 的前身)编译代码。编译器会向生成的二进制中植入极低开销的轻量插桩;当运行时触发未定义行为的那一刹那,程序会直接崩溃并精准指出触发点,而不是任由未定义行为继续破坏状态。
Chandler 强调,由编译器辅助的动态分析技术才是未来的方向:
> “For reference, you can take AddressSanitizer, you can use it to secure Chrome, a web browser. You can load it. You can browse the web interactively. You don't even notice the slowdown. That's the kind of dynamic instrumentation we want because that's what we can use to catch real bugs in pervasive situations.”
- **AddressSanitizer (ASan)**:在编译期插桩内存访问,覆盖堆、栈和全局变量,运行开销极低。
- **ThreadSanitizer (TSan)**:通过编译器插桩监控内存读写与同步原语,用于发现多线程数据竞争。
- **性能标杆**:AddressSanitizer 已经可以直接编译大型软件(如 Chromium/Chrome 浏览器),用户交互式浏览网页时完全感知不到卡顿。
---
### [043:20] C++11 的进化与新语言特性带来的潜在陷阱
演讲的落脚点回到了 C++ 语言本身,特别是 C++11。Chandler 原本准备了大量幻灯片来展示 C++11 如何通过语言特性减少前文提到的各种常见 Bug,但此前 Bjarne Stroustrup 和 Herb Sutter 已经极其精彩地展示了现代 C++ 的优雅与强大,几乎覆盖了他准备的所有例子。C++11 带来了更安全的内存管理、线程库与并发原语,让代码的正确性与可预测性迈上了一个全新台阶。
然而,新特性的引入也伴随着新的未知风险。Herb Sutter 此前极力推荐广泛使用 `auto` 关键字来推导类型、消灭冗余,但在某些特定场景下盲目套用建议却会导致极其荒唐的 Bug。例如一段由 Richard Smith 提供的代码示例:如果原本显式声明了变量类型,程序运行完全符合预期;但如果仅仅是将显式类型机械地替换为 `auto` 并配合大括号花括号列表初始化(Braced initialization),这段代码在运行时就会直接触发断言失败(Assert)。
Chandler 略带歉意地向听众坦白,这个例子甚至没法在 Clang 上演示,因为通用的初始化列表(Generalized initializer lists)在当时的 Clang 中还没完全实现好,团队正在全力赶工。更关键的问题在于,现有的编译器并没有对此发出任何警告或诊断信息。`auto` 与花括号初始化结合后推导出了 `std::initializer_list` 类型,而从后续代码对该变量的使用方式来看,程序员压根不可能希望得到一个初始化列表对象。编译器完全有能力通过静态语义去审查变量的实际使用模式,发现这种极其可疑的隐式类型推导,并为开发者主动弹出警告。面向 C++11 的安全性工具与静态检查规则库,仍然需要社区继续深入构建。
### [45:36](https://www.youtube.com/watch?v=NURiiQatBXA&t=2736s) C++11 的安全性攻坚:编译器警告与新语言陷阱
Chandler Carruth 指出,针对 C++11 设计诊断警告是确保这门新语言安全性的下一个重大战役。C++ 标准委员会已经尽职尽责地打造了一门尽可能安全和行为可预测的语言。然而,随着业界实战经验的积累,后续必须双管齐下:一方面向委员会反馈、修复语言本身的设计缺陷;另一方面,开发者必须要求编译器实现者提前排查陷阱,主动守护正在学习 C++11 的工程师们。“It's very important that as implementers of C++ 11 that we race ahead that we race ahead and find these pitfalls early and get warnings in place so that as people are learning C++ 11, as we are trying to get adoption of the language, people are not scared away by pitfalls that are easily handled and far more minor than the pitfalls of the slides you've just seen.” 尽早建立完善的警告机制,才能防止新手被本可轻易规避的坑绊倒,从而扫清语言推广采纳的阻碍。
随后,Chandler 分析了 C++11 中几个非常诱人却潜藏危机的特性。首先是 `decltype`:人们为了摆脱繁琐的类型名称而青睐 `decltype`,但在某些特定表达式下,盲目使用它并不能得到你期望的类型;若表达式涉及模板推导,其推导结果可能令人大吃一惊,例如意外推导出右值引用(rvalue reference)等其他类型。其次是右值移动语义(move semantics):它虽然能大幅减少不必要的对象拷贝,但由于引用本质上依然只是引用,过度移动可能移走局部变量、产生悬挂指针(dangling pointers)。即便这些新错误模式在 C++11 中出现的频率较低,人类肉眼也极难发现,必须依赖编译器的警告体系提供支持。
```mermaid
flowchart TD
accTitle: C++11 特性落地与编译器防线
accDescr: 展示 C++11 推广落地中标准委员会、编译器实现与开发者的协作关系
subgraph Evolution["标准演进与反馈"]
A["标准委员会制定规范"] --> B["业界实战反馈"]
B --> C["修复语言标准缺陷"]
end
subgraph Tooling["编译器实现层 (Clang)"]
B --> D["提前排查语言新陷阱"]
D --> E["decltype 意外类型警告"]
D --> F["移动语义/悬挂引用警告"]
D --> G["常量表达式空指针警告"]
end
subgraph Adoption["开发者落地"]
E & F & G --> H["避免工程踩坑耗时"]
H --> I["建立信任并加速采纳"]
end
```
### [47:53](https://www.youtube.com/watch?v=NURiiQatBXA&t=2873s) 最诡异的边缘陷阱:当常量表达式坍缩为 NULL 指针常量
Chandler 展示了一个令他感到恐惧的极端边缘案例。这种代码在日常开发中绝不会每天发生,可能“百年难遇”(once in a blue moon),但正因无法在语言规范层完全杜绝,才必须由编译器捕获并明确告警。屏幕上呈现了一段测试代码,Chandler 询问现场观众程序会输出什么,在无人回应后他改问:“你们希望程序输出什么?”台下有人回答希望输出 `X`。Chandler 透露,在 C++98/03 的旧规范下,该程序确实会调用相应函数构造 `X` 并打印出 `X`;然而在 C++11 下,程序语义发生了不可思议的突变,它不再构造 `X`,而是命中了一个截然不同的重载分支,其原因在于底部的某个表达式被判定为了空指针常量(null pointer constant)。
这一诡异结果源自 C++11 通用常量表达式(generalized constant expressions, `constexpr`)、默认初始化规则以及向后兼容 C++98 遗留空指针字面量规则三者的交织。Chandler 强调,尽管没有任何程序员会刻意写出这种代码,可一旦在复杂的模板调用或宏展开中偶然触发,开发者就会耗费数小时排查却完全摸不着头脑。“This may seem like an edge case, but the time lost when it happens is massive, right? And it shakes confidence in the language. It makes people distrust C++.” 这种隐晦故障会极大地打击开发者对 C++ 的信任感,编译器必须通过精准的诊断把背后的底层缘由清晰告知用户并指导修复。随后,Chandler 顺势为 Clang 项目做了一次诚恳的开源宣传,呼吁大家参与贡献代码:“Clang is unique in that it's an open source compiler that's written in C++ that's a very friendly codebase and has a very friendly community. Please come submit patches to it.” 他声明 Clang 社区欢迎补丁绝非一句敷衍的托辞,而是真正渴望更多开发者共同将其打磨得更好。
- 触发问题的三种机制交织:C++11 泛化常量表达式(generalized constant expressions)、默认初始化规则(default initialization rules)、兼容 C++98 的旧式空指针常量语义。
- 危害特征:极难被肉眼排查,一旦触发将导致工程师损失数小时调试时间,严重动摇对 C++ 语言可靠性的信任。
- 社区号召:Clang 是使用现代化 C++ 构建的开源编译器,拥有极其友善的代码库和社区文化,急需更多开发者提交 patch 助力生态安全。
### [50:38](https://www.youtube.com/watch?v=NURiiQatBXA&t=3038s) 问答深入:解析常量表达式规则漏洞与编译链接耗时挑战
在 Q&A 环节,现场第一位提问者希望 Chandler 能重新拆解并详细解释刚刚那个空指针案例的代码机制。Chandler 坦言:“Richard Smith and I worked on this one for a day or two to make sure we like we think we understand it. We may still be wrong. All right. Just like Howard said, this is a new language. We are all learning about these interactions.” 他解释道,代码中的结构体 `S` 是一个聚合体(aggregate),因此获得了一个可用于常量表达式的默认构造函数,其默认构造语义在常量表达式上下文内部会执行零初始化(zero-initialization)。当在代码下方构造临时对象 `S()` 并访问其成员 `n` 时,由于上下文可能处于常量表达式求值路径中,整条链路完全符合常量表达式规则。最终,深入到底层解析出的 `n` 变成了一个编译期常量值 `0`。在标准对空指针常量的定义中,这个整型常量值 `0` 与源码中显式手写的字面量字符 `'0'` 没有任何区别,编译器只能将其视为空指针常量并选定匹配指针的函数重载分支;而在 C++98 中,由于没有泛化常量表达式,默认构造的 `S().n` 绝不会被提升为编译期常量,因而会老老实实通过隐式类型转换去匹配生成 `X`。
现场观众紧接着追问,除了在编译器中增加警告之外,这显然属于语言标准层面的 bug,他们是否已经向委员会提交了缺陷报告(defect report, DR)。Chandler 笑着透露,这个例子其实是 Richard Smith 在本周一才刚刚捣鼓出来的,他们目前尚未提交缺陷报告,而且由于涉及历史兼容性包袱,要在规范里干净利落地修复这一漏洞极其棘手。随后另一位与会者抛出了一个现实工程痛点:编译器做如此多深度静态分析和复杂语法推导,是否会大幅增加代码的编译耗时,尤其是重度使用模板带来的链接时间膨胀(link times)。Chandler 回应称,他的同事前一晚刚跑出了 Clang 最新的性能基准测试图表,可惜没来得及塞入幻灯片。数据表明,对于大量依赖现代模板特性的重度 C++ 代码,Clang 的前端编译速度大约是 GCC 的两倍,2 倍的提速在编译时间优化领域是一个巨大的飞跃;至于链接时间问题,Chandler 表示业界正在探讨一系列全新的解决方案和提案。
- 表达式坍缩路径:聚合体 `S` 的隐式默认构造函数满足 `constexpr` 规范 $\to$ 成员零初始化 $\to$ `S().n` 成为常量求值上下文中的整型 `0` $\to$ 满足 C++98 继承而来的空指针常量定义 $\to$ 优先匹配指针类型重载,绕过了用户预期的 `X` 转换构造。
- 缺陷报告状态:该案例由编译器专家 Richard Smith 在演讲当周周一刚挖掘发现,尚未正式提交 ISO C++ 委员会 Defect Report,修复方案牵扯甚广。
- 编译吞吐性能对比:在模板重度密集、结构复杂的 C++ 编译场景下,Clang 的编译速度可达 GCC 的 2 倍左右(2x faster)。
### [54:34](https://www.youtube.com/watch?v=NURiiQatBXA&t=3274s) 模块化与链接器性能的重新思考
Chandler Carruth 在回答提问时探讨了 C++ Modules(模块)如何通过提前完成更多链接工作来发挥潜力。他指出,模块可以让动态库变得更加丰富,使得软件能够“一次链接、直接分发”,用户只需引用即可。不过,他认为当前链接器性能低下的核心原因并非是任务量过大,而在于人们过去从未以足够激进的思维方式从根本上去重新审视二进制链接问题。
在 Chandler 看来,现有的链接技术严重受限于对过去二三十年乃至四十年历史包袱的兼容。他感慨道,链接器常常被死死限制在沿用 1975 年被认为最优的文件格式与规范中,而在当下这显然已经不再适用。只要愿意抛开历史枷锁、从零开始重新审视这一问题,链接速度完全有希望实现数量级(orders of magnitude)的飞跃。Chandler 提到,LLVM 体系内当时正在开发一个全新的链接器项目(lld),尽管该项目第一版可能不会立刻完全推翻旧规则,但他依然对通过激进设计革新链接效率抱有很大的期待。
Chandler Carruth 明确指出:“I think that our current linkers are slow because we haven't yet fully studied the problem of how to link binaries efficiently and we haven't yet done that study in a in a radical enough mindset.”
```mermaid
flowchart TD
accTitle: 现代链接器性能瓶颈与重构思路
accDescr: 对比传统链接器受历史包袱限制与从零开始激进设计的性能提升路径
subgraph Legacy["传统链接器的瓶颈"]
L1["背负 20-40 年历史兼容包袱"] --> L2["沿用 1975 年代的文件格式与规范"]
L2 --> L3["链接耗时极长且难以根本优化"]
end
subgraph Modern["激进革新路线 (如 lld / Modules)"]
M1["Modules: 提前分担链接工作"] --> M2["重新审视底层二进制链接逻辑"]
M2 --> M3["实现数量级 (Orders of Magnitude) 性能飞跃"]
end
```
### [55:52](https://www.youtube.com/watch?v=NURiiQatBXA&t=3352s) 空指针警告与历史代码中的奇葩写法
现场观众提出一个跟进建议:如果大家当初都能直接使用 `nullptr` 而不是 `0` 或 `NULL`,很多歧义问题便不复存在;因此编译器是否可以针对“值非直观写作 0 的常量表达式”(如 `1 - 1` 或某种非 0 命名的常量)充当空指针的情况发出警告。Chandler 回应称,Clang 的核心开发者 Richard Smith 正是在发现这类陷阱后,在 Clang 中实现了该警告检查。
当 Clang 团队在海量现有代码库上运行这项检查时,结果令人大吃一惊:该警告触发的频率极高。检查扫出的代码呈现出两极分化的景象——一部分确实是漏洞百出、隐藏极深的 Bug;但另一部分则是开发者为了规避预处理器宏,特意在 C++98 中定义的非字面量命名常量。例如,团队发现大量 C++98 代码会显式定义一个整型常量 `const int kNull = 0;` 来指代空指针。虽然这种模式在现代看来非常可疑且并不规范,但它确实广泛存在于合法编译的代码中。Chandler 表示尽管如何最妥善地修复这些旧代码仍有待权衡,但这项警告功能最终大概率会正式合并进入 Clang。
### [57:04](https://www.youtube.com/watch?v=NURiiQatBXA&t=3424s) Clang 缺乏 Windows 支持的原因与现实困境
当被现场观众问及 Clang 为何在 Windows 平台上难以良好工作时,Chandler 坦率地对台下的 Windows 开发者们倒出了苦水。他直言不讳地指出了最根本的原因:“One of them is quite frankly Visual C++ is a really good compiler.” 正因 Visual C++ 本身在 Windows 上表现优异,他在演讲开篇所展示的那些让 GCC 用户痛不欲生的错误提示和痛点,在大部分 Windows 开发者身上根本不存在,这也导致社区缺乏足够的内在驱动力去投入极其庞大的精力为 Clang 移植 Windows 支持。
除了缺乏驱动力,专业人才和基础设施的匮乏构成了第二道死穴。Clang 核心社区几乎没有人精通 Windows 底层机制与生态,极少数尝试推进 Windows 支持的开发者面临着浩如烟海的工作量,却无法从外部获得有效支援。Chandler 自嘲道,自己甚至连提交进来的 Windows 相关补丁都无法评审,因为他根本无法判断其代码正确与否。与此同时,目前赞助开源项目基础设施的两家主要公司在 Windows 基础设施构建上非常外行,导致测试流水线极度薄弱。开源项目生存依赖于“补丁”与“基础设施”,Chandler 现场向参会者发出呼吁,急切期盼有深厚 Windows 背景的开发者和企业加入进来提供帮助。
- **MSVC 优势壁垒**:Visual C++ 自身表现极佳,Windows 开发者迁移到 Clang 的痛点与意愿不如类 Unix 平台强烈。
- **人才断层**:Clang 核心团队缺乏 Windows 专家,甚至无法有效 Code Review Windows 特定的代码补丁。
- **基础设施缺乏**:没有大厂在 Clang 开源生态中提供稳定、专业的 Windows CI/CD 构建基础设施。
### [59:02](https://www.youtube.com/watch?v=NURiiQatBXA&t=3542s) 诊断建议(Fix-its)与 C++11 现代化的采纳准则
最后一位提问者询问,Clang 在提供错误(Errors)和警告(Warnings)之余,是否设想过为开发者提供主动优化建议(Suggestions),比如提示用户“此处可以使用 initializer list 代替”、“可以使用标准算法替换循环”或“此处可以使用 `auto`”,以便帮助开发者平滑过渡到现代 C++ 的最佳实践中。
Chandler 给予了高度肯定的回答,并深入拆解了 Clang 诊断引擎中“修复建议”(Fix-it Hints)的严格机制。他解释说,Clang 对错误与警告的建议策略有着泾渭分明的界限:
1. **错误(Error)诊断中的修复**:Clang 只有在能进行“恢复性解析”(Recovery Parsing)时,才会把建议直接绑定在错误本身上——即编译器在报错的同时,能够假定用户采纳了该建议并继续向下正确解析语法。如果不满足这一点,编译器绝不轻易给出建议。Chandler 强调,学术界对此有大量研究,编译器如果无休止、无把握地给出修正,极易导致后续解析全线崩溃脱轨。
2. **警告(Warning)诊断中的建议**:由于警告本身属于语法合法的代码,编译器不能直接拒绝程序,也不能强行介入恢复解析。因此,针对警告的优化建议全部以附带附注(Notes)的形式展现,作为供开发者自选的修复方案。
Chandler 指出,Clang 必须为新引入的 C++11 警告配套提供明确的操作选项(例如直接建议替换为 `nullptr` 等)。编译器必须确保每一条给出的修复建议都代表了社区公认、毫无歧义的最佳实践。在全场热烈的掌声中,主持人宣布演讲结束,感谢 Chandler Carruth 带来的精彩分享。
## Notable quotes
> [33:03](https://www.youtube.com/watch?v=NURiiQatBXA&t=1983s) Every time we can extend the language to codify the semantics, we can actually get improved checking from our tools and especially our compilers. — Chandler Carruth
> [37:07](https://www.youtube.com/watch?v=NURiiQatBXA&t=2227s) You don't you don't add complexity to your code to catch bugs. You simplify it to catch bugs. But as we simplify our code, Valgrind ceases to be able to analyze it effectively. — Chandler Carruth
> [33:03](https://www.youtube.com/watch?v=NURiiQatBXA&t=1983s) We're doing full-blown static analysis during your compilation to give you diagnostics about what is wrong with the behavior of your program. — Chandler Carruth
> [21:24](https://www.youtube.com/watch?v=NURiiQatBXA&t=1284s) It doesn't matter what you're storing the value into. You have to actually compute the right value first. — Chandler Carruth
> [54:34](https://www.youtube.com/watch?v=NURiiQatBXA&t=3274s) I think that our current linkers are slow because we haven't yet fully studied the problem of how to link binaries efficiently and we haven't yet done that study in a in a radical enough mindset. — Chandler Carruth
## Mentioned
Chandler Carruth, Chris Lattner, Richard Stallman, Herb Sutter, Charles Torre, Andrei Alexandrescu, Bjarne Stroustrup, Richard Smith, Google, Apple, Clang, LLVM, GCC, Visual C++, Valgrind, AddressSanitizer, ThreadSanitizer, UndefinedBehaviorSanitizer, lld, Chrome, Chromium, C++11, C++98, Linux, Mac OS X, Windows
## Video description
Chandler Carruth - Clang: Defending C++ from Murphy's Million Monkeys
[Source](https://www.youtube.com/watch?v=NURiiQatBXA)
Source quotes
[33:03] Chandler Carruth: Every time we can extend the language to codify the semantics, we can actually get improved checking from our tools and especially our compilers.
[37:07] Chandler Carruth: You don't you don't add complexity to your code to catch bugs. You simplify it to catch bugs. But as we simplify our code, Valgrind ceases to be able to analyze it effectively.
[33:03] Chandler Carruth: We're doing full-blown static analysis during your compilation to give you diagnostics about what is wrong with the behavior of your program.
[21:24] Chandler Carruth: It doesn't matter what you're storing the value into. You have to actually compute the right value first.
[54:34] Chandler Carruth: I think that our current linkers are slow because we haven't yet fully studied the problem of how to link binaries efficiently and we haven't yet done that study in a in a radical enough mindset.
Source quotes
[21:24] Chandler Carruth: It doesn't matter what you're storing the value into. You have to actually compute the right value first.
[33:05] Chandler Carruth: Every time we can extend the language to codify the semantics, we can actually get improved checking from our tools and especially our compilers.
[9:17] Chandler Carruth: Murphy is not a passive player here. He is actively seeking out the flaws in your code.
[2:23] Richard Stallman: One of the main goals for GCC is to prevent any parts of it from being used together with non-free software. Thus, we have deliberately avoided many things that might possibly have the effect of facilitating such usage.
[59:02] Chandler Carruth: Clang will only actually attach a suggested fix to the diagnostic itself when the diagnostic is an error and when it can actually recover from that error by parsing as if the user wrote what it suggested.
Original transcript
[0:00] And one of the things I think is really
[0:02] cool about this is uh Chandler sort of
[0:05] he embodies the young the next the new
[0:08] generation. So um he owns Clang at
[0:12] Google and he's going to come out here
[0:14] and teach us about Clang. I don't know
[0:15] most of you have probably used it maybe
[0:17] not but come on out here Chandler.
[0:26] All right so I'm here today to talk to
[0:28] you a little bit about Clang. I want to
[0:29] thank uh Herb and Charles for actually
[0:31] inviting me to come talk to you about
[0:32] Clang. It's it's a really great
[0:34] opportunity. It's a great opportunity to
[0:35] get to talk to a lot of people who who
[0:37] may not be exposed to Clang and in kind
[0:39] of their day-to-day interactions with
[0:41] C++. Um just as kind of just just to get
[0:45] a feel for things. How many folks here
[0:46] have ever used Clang like even once?
[0:51] All right. How many folks here have used
[0:53] Clang in the last month?
[0:55] How many folks here used Clang because
[0:57] of this talk?
[0:59] All right, some of you aren't being
[1:00] honest. Okay, that's okay. So, CL C
[1:03] clang is really interesting and and
[1:04] because it's it's very new and because
[1:06] it doesn't support as many platforms,
[1:08] it's still gaining a lot of traction. I
[1:09] want to try and give you guys some
[1:10] background on on what Clang is, where
[1:14] it's come from. So, let's let's talk a
[1:16] little bit just very generally about
[1:17] Clang. Um, now the first thing I really
[1:20] want to talk about Clang is where this
[1:22] came from. Why did we set out to build
[1:23] another C++ compiler? we have quite a
[1:26] few C++ compilers out there. Why are we
[1:28] building another one? And there like
[1:30] this isn't an easy task either, right?
[1:33] Why did we choose to do that? And to
[1:35] understand that, you have to understand
[1:36] the C++ compiler that a lot of people in
[1:39] the world were dealing with. Uh it it
[1:40] wasn't nearly as as uh nice to interact
[1:43] with as Microsoft's visual C++ compiler.
[1:46] I wish that it were. Um and you could
[1:48] tell this because you would write code
[1:50] and you know, you would make some
[1:51] mistakes. You would leave out some curly
[1:53] braces. you'd add some in in the wrong
[1:54] places and you would compile your code
[1:56] and you would get something that looks
[1:58] like this. Now, I don't know about you,
[2:00] but having tokens listed does not help
[2:03] me correct my code. All right, this is
[2:05] not useful, right? And that that was
[2:07] just the beginning of the problems that
[2:09] we we have with the existing open source
[2:11] compilers, right? Because they're also
[2:13] it's also pretty slow and and it's no
[2:15] fun to sit around waiting for your
[2:17] compiles to finish, right? This is not
[2:19] this is not what we want out of our
[2:21] compiler, right?
[2:23] And and then we get to the really
[2:24] fundamental thing we want from our
[2:26] compiler that we just do not have in the
[2:29] open source world because what we want
[2:31] from our compiler is we want we want
[2:34] tools from our compiler. The compiler is
[2:36] a beautiful tool but it is not the only
[2:38] tool. We need more than just a compiler
[2:40] but we don't want to rewrite the
[2:42] understanding of C++ every time we write
[2:45] a tool. We want to use the same
[2:46] understanding. And you know this is a
[2:49] common theme. You can see it in any IDE.
[2:51] You can see it in any tool that's really
[2:53] helping developers with C++.
[2:56] But when you go and you ask, if you go
[2:57] and you know, as someone did, and this
[2:59] is a real quote from the GCC mailing
[3:01] list, by the way, I'm not I'm not
[3:02] putting words in anyone's mouths here.
[3:04] If you go and ask, why can't we take
[3:07] GCC's front end and use it use it as a
[3:10] library? Use it to build developer
[3:12] tools, you'll get a response that is not
[3:15] useful.
[3:17] One of the main goals for GCC is to
[3:21] prevent
[3:23] any parts of it from being used together
[3:25] with non-free software.
[3:28] Thus, we have deliberately avoided many
[3:31] things that might possibly
[3:33] possibly have the effect of facilitating
[3:36] such usage. That is a direct quote from
[3:39] Richard Stallman on the GCC mailing
[3:40] list. Now, this is not a useful answer.
[3:44] This is a political answer. This is a
[3:46] social answer and is not a useful answer
[3:48] and we cannot build our tools with this.
[3:51] This does not work. Okay, this makes
[3:55] absolutely no sense. What are we even
[3:57] doing with this compile? Okay, so
[4:02] we we need something better. All right,
[4:05] so so of course, you know, we don't
[4:06] we're not going to sit on our hands. So
[4:08] we tried to build C++ tools and we tried
[4:10] to build them with GCC. We tried very
[4:12] hard, but very early on we ran into code
[4:14] that looks like this. This isn't even
[4:16] C++. This is C. This is bad C code. And
[4:19] we just tried to build a simple tool to
[4:20] deal with this. And we asked GCC to
[4:23] analyze this source code to tell us
[4:24] about it, to tell us how it worked. And
[4:26] GCC told us that it worked like this.
[4:30] We gave it this source code and GCC came
[4:32] back. It's like, yeah, you gave us this
[4:34] source code. That's not the same thing,
[4:36] right? Do you see this is not the same
[4:39] source code GCC thinks it is. The parser
[4:42] inside of GCC. The parser does trivial
[4:46] constant folding.
[4:48] When it sees plus zero, it throws the
[4:51] tokens on the floor and keeps going.
[4:54] There's no a there's no semantic model
[4:57] for this. You can't figure out what has
[4:59] happened here. This makes implementing
[5:01] even the most trivial of C++ tools
[5:04] impossible with GCC. It's not suited to
[5:07] the task. So, we needed something
[5:09] better. We needed something much better
[5:11] because given this response, what are we
[5:14] to do? Like, if you if you lie to me
[5:16] about what my code says, I can't use
[5:19] this. You are making it far too easy to
[5:21] point the gun at my foot. You're making
[5:23] you're practically pulling the trigger
[5:25] for me. I can't stand th this type of uh
[5:28] manipulation of the C++ code. It doesn't
[5:31] work. Okay. So, we need something
[5:32] better. Fortunately, there is a group
[5:34] that was running into this called the
[5:35] LVM community, a wonderful group um that
[5:38] Chris Latner built and and has continued
[5:40] to build at Apple and they were hitting
[5:42] all of these problems and they wanted to
[5:44] find a solution as well. And Chris
[5:45] Latner in and I think the truest spirit
[5:47] of open source said, "I know how to
[5:49] solve this problem. I'll write a new
[5:51] one." Right? We can we can solve this
[5:52] through code. Right? And so we've got
[5:55] Clang. This is where Clang came from.
[5:57] came purely motivated out of frustration
[5:59] and inability to accomplish the goals of
[6:02] anyone trying to support real C++
[6:04] development. All right, and this is how
[6:06] we get clang. Now, just just to give you
[6:08] a background on where Clang is today.
[6:10] All right, let's let's talk about the
[6:11] kind the state of this compiler. Okay,
[6:13] the state of Clang is that it is a fully
[6:16] functioning C++ 98 compiler. It works.
[6:19] We believe it to be essentially as
[6:21] correct as we would like it to be. It's
[6:23] relatively bug free. We're very happy
[6:24] with it, right? is as bug free as you
[6:26] know any compiler that's as young as it
[6:27] is is going to be right. We're we're
[6:29] very happy with it for C++ 98. It works
[6:32] very well on Linux. It works very very
[6:34] well on on Macintosh. It it doesn't work
[6:37] so well on Windows. I'm very sorry. Um
[6:39] we'll get to that later. Uh so but it's
[6:42] it's a it's a good compiler. We're happy
[6:43] with it. It's it's been uh the latest
[6:45] release was a very good release for C++
[6:47] 98 code. Now that's the current state,
[6:51] right? That's where we are. It's it's
[6:53] factored. It's libraries. We can build
[6:54] tools on it. We're actively building
[6:56] tools on it. But let's also think about
[6:58] what's coming next because we've got
[7:00] some nice breaking news here. We've got
[7:02] some important developments with clang.
[7:04] Clang is much further along with C++ 11
[7:07] than a lot of people expected. All
[7:09] right. And and this is this was very
[7:10] surprising to me actually and I'm
[7:12] actually one of the developers. When I
[7:13] started really assessing it, the the
[7:15] number of things that are missing in
[7:16] clang is very small. We are missing
[7:18] initializer lists. We are missing the
[7:21] last few pieces of constant expressions.
[7:23] We're missing lambdas. We're missing uh
[7:26] let's see, we're missing a couple of
[7:27] other things. We're missing uh
[7:29] unrestricted unions. We're missing a
[7:31] couple like very few things. Now,
[7:33] they're important ones. The really fun
[7:36] thing is that I believe the list of uh
[7:38] really big and important things that are
[7:40] going to be missing from the next
[7:41] release of Clang is likely, no promises,
[7:44] but likely to be unrestricted unions, uh
[7:47] inheriting constructors and atomic
[7:51] support, concurrency support.
[7:53] Concurrency support is something we're
[7:54] all learning. We're going to have the
[7:55] rudimentary atomic support, but it's
[7:57] very rudimentary. This is something
[7:58] we're going to be working on for a long
[7:59] time to come. There'll be some other
[8:01] smaller features missing, but we're
[8:02] actually going to have a a generally
[8:04] complete thing. We expect to have
[8:05] constant expressions, initializer lists,
[8:07] and lambdas in the next version of
[8:09] Clang. So, it's really, really, really
[8:11] close to the edge of C++ support. All
[8:13] right, we're really, really happy with
[8:14] this. Now, keep in mind, keep in mind I
[8:16] said maybe, and also they're going to be
[8:18] bugs because these features are very new
[8:20] and they're very large. And one of the
[8:21] interesting things about clang is that
[8:23] as an open source compiler, we we are
[8:25] going to launch very early, right?
[8:27] You're going to see features very very
[8:28] early on. You're going to see them when
[8:29] they're a little bit rough, and we're
[8:30] going to keep working on them. Now,
[8:34] we need to also talk about something a
[8:35] little bit different though because
[8:36] that's that's that's the history and the
[8:38] background on clang. Now, you know where
[8:39] Clang is, but I also need to tell you
[8:41] about a problem that we're facing today.
[8:44] An ongoing problem. This isn't just the
[8:45] problem that led to Clang. And it's one
[8:47] of the biggest problems that uh Google
[8:49] has faced and one of the bigger problems
[8:51] that a lot of other companies have faced
[8:52] with C++. And this is this is what we're
[8:54] going to talk about for the rest of this
[8:56] uh lecture. And that problem has to do
[8:58] with Murphy. So, we're going to talk
[9:00] about Murphy. We're going to hope hope
[9:01] hope sincerely that he's not paying too
[9:03] much close attention for at least the
[9:04] next hour. So Murphy Murphy's law is
[9:08] very very prominent in programming,
[9:10] right? Things go wrong. We all know
[9:11] this. But you need to understand the
[9:14] depth to which things can go wrong when
[9:16] you're working with a language like C++.
[9:17] If you're not careful, if you don't have
[9:19] all of the safety mechanisms in place,
[9:22] things can go very wrong. Because Murphy
[9:24] is not passive in this, right? Murphy is
[9:26] not a passive player here. He is
[9:29] actively seeking out the flaws in your
[9:32] code. Murphy is an angry angry dude. All
[9:35] right, for reference, this is like a UK
[9:38] television series. I know nothing about
[9:40] it, but apparently it's got an angry
[9:41] Murphy, and that's that's what I like
[9:43] because Murphy can sneak up on you if
[9:45] you're not paying attention. And when
[9:46] Murphy sneaks up on you, the
[9:47] consequences are not mild. They're not
[9:49] small. They're disastrous. Okay? So, we
[9:52] need to be very very attentive
[9:54] when we're actually defending against
[9:56] Murphy. We need to be very careful when
[9:58] we're trying to make our languages safe.
[10:00] Right? So throughout this talk when I'm
[10:02] talking about Murphy, when I'm talking
[10:03] about things I really want you to keep
[10:05] in mind, right? Like you know an angry
[10:08] angry Murphy looking to cause a massive
[10:10] disaster. That's what we're trying to
[10:12] avoid here. Okay. So with that
[10:14] perspective, there's one other element
[10:16] that really impacts the type of problems
[10:19] we see with C++. Um C++ is actually a
[10:22] very safe language. It's very safe. In
[10:24] fact, if if all we had to worry about
[10:26] was like one programmer working in C++
[10:28] and worrying about, you know, Angry
[10:29] Murphy and the disasters that can come
[10:31] from that, I would actually be very
[10:32] happy. But the problem is we don't have
[10:34] one. We have a lot more than one. We
[10:36] have lots and lots of programmers all
[10:38] sitting around hammering away at
[10:39] keyboards constantly. And that means
[10:41] that the chances of something going
[10:43] wrong go up. And we need to take a lot
[10:44] of extra steps to try and fight against
[10:47] that. And one other thing I do want to
[10:48] mention, uh, sorry, I have I have this
[10:51] disaster slide. I don't necessarily mean
[10:54] uh like fighter jets. I don't mean oil
[10:57] rigs. The disaster could be missing a a
[11:00] ship date, right? The disaster could be
[11:02] purely in terms of developer
[11:03] productivity. It could be in terms of,
[11:05] you know, developers understanding the
[11:06] source code, but it could actually still
[11:08] be disastrous if it's a pervasive
[11:10] problem. Okay? And this is something
[11:11] that's very, very important because we
[11:13] have a lot of developers and they're all
[11:14] struggling against fairly simple and
[11:18] small problems that we need to help them
[11:19] out with wherever we can. Okay? And this
[11:22] becomes even more relevant as the number
[11:24] of developers magnifies. Right? As you
[11:25] grow, the number of code monkeys you
[11:27] have access to. All of them are
[11:29] collaborating and you get a herd of
[11:31] monkeys, all of them trying to to find
[11:35] correct C++. So we need to help out the
[11:38] code monkey some. And the way that we
[11:41] found to do that uh with clang is is
[11:44] pretty interesting because it's actually
[11:46] taking it back. We actually kind of went
[11:47] back to our basics. So let's talk about
[11:49] diagnostics. All
[11:51] right, let's talk about diagnosing bad
[11:54] C++ code. All right,
[11:57] so this is some bad C++ code. Um, it's
[12:00] pretty obvious what's wrong with this if
[12:01] you if you know what you're looking for
[12:03] here, but if you're typing fast, if
[12:05] you're working, if you're in a hurry,
[12:06] you can miss it. Maybe you miss it the
[12:08] first time. The important thing is
[12:10] understanding what's gone wrong, when
[12:12] you miss it, when it gets into your
[12:14] program and your compile fails. And
[12:16] Clang helps with that. Because with
[12:18] Clang, what you get is a very detailed
[12:22] explanation of what's going on. And now
[12:24] I'm going to take take a minute to
[12:25] actually explain the structure of
[12:27] Clang's diagnostics. Right? The first
[12:29] thing you're going to see is an error
[12:30] message. Right? Clang's trying to say,
[12:32] okay, here is what has gone wrong. Here
[12:34] is what makes this program an invalid
[12:37] program. You've declared an array and
[12:39] has a negative size. And then Clang is
[12:41] going to start giving you extra
[12:42] information. Okay? So it's shown you the
[12:44] array, right? It's shown you the array
[12:45] and the invalid size. It's underlining
[12:47] everything. It's highlighting exactly
[12:49] what's going on there. But we actually
[12:50] don't understand this because what what
[12:52] is in why is that negative? We need more
[12:54] information, right? Okay. So, it says
[12:57] that it's in an instantiation. It tells
[12:59] you exactly what instantiation. It's in
[13:00] an instantiation of a class, right? And
[13:03] it's going to tell you what n is. N is
[13:05] four here. Okay. Now, I am starting to
[13:07] understand why something has gone
[13:08] horribly wrong with my program. I'm at
[13:10] 42, not four, clearly. My bad. Right?
[13:13] So, we're starting to slowly break down
[13:16] exactly what's gone wrong. And Clang is
[13:18] going to step back through every layer
[13:19] of the templates with you. All right?
[13:21] It's going to walk up the stack, show
[13:23] you how your code actually ended up
[13:26] invalid. And this is incredibly
[13:28] powerful. Okay? It's incredibly
[13:30] incredibly powerful.
[13:32] Now, if you think that's amazing for
[13:33] templates, macros are much more
[13:36] frustrating. And as much as I would
[13:37] really I I I join Herb in in hoping that
[13:41] there is a world without macros, we
[13:42] don't live in that world. And
[13:44] unfortunately, we use libraries that
[13:46] were not written in that world. And
[13:47] those libraries impose upon us macros.
[13:49] We don't even know that they're macros.
[13:51] Half the time we end up using them. And
[13:53] so we need help. We need help
[13:54] understanding the macros as well. Now,
[13:56] the nice thing is that clang provides
[13:58] the same type of contextual information
[14:00] when diagnosing errors out of macros.
[14:03] It's going to show you the error, but
[14:06] it's going to show you exactly how it
[14:08] formed that error textually in the
[14:11] pre-processor. Clang is walking back
[14:13] through each layer of the macro
[14:15] expansion, showing you h what the next
[14:17] step is and exactly where in that next
[14:20] step that erroneous piece of code lived.
[14:24] So, we can even see that, you know, we
[14:25] know that it's this middle argument to
[14:27] the macro that's bad. we know that that
[14:29] middle argument is used in another layer
[14:31] of macro expansion and we can show
[14:33] exactly what it got expanded into in the
[14:35] at the final uh stage. This is
[14:38] incredibly powerful for understanding
[14:40] exactly how bad code got into your code,
[14:43] right? You didn't write it, you wrote
[14:45] M2, but client can actually show you
[14:47] what happened. And that's that's very
[14:48] very powerful.
[14:50] Now, then we get into more more less
[14:53] powerful things and more immediately
[14:55] gratifying things because sometimes I
[14:58] I'm actually a particularly bad person
[14:59] here. We're working really fast and
[15:01] we're not paying a lot of attention and
[15:02] we have typos. Typos are the bane of
[15:06] programmers, but we have amazing tools
[15:08] to deal with typos and clang because we
[15:09] see that, you know, there's a base type
[15:11] here. Now, this one's tricky. I want you
[15:13] to look really carefully. There are two
[15:15] base types in this code. One of them is
[15:18] a variable. One of them's a type. Which
[15:21] one were we aiming for? Well, Klein
[15:23] knows that we can't be aiming for a
[15:25] variable. It's a static variable. You
[15:26] can't use a static variable in a member
[15:28] initializer. And so, it knows certainly
[15:30] that you meant to actually name the
[15:32] type. And it tells you that you meant to
[15:34] name the type. And in fact, it continues
[15:36] parsing the rest of the file as if you
[15:37] had named the type so that all the other
[15:39] error messages are just as accurate. All
[15:42] right. And it's going to show you how to
[15:43] fix your code, exactly where it got this
[15:45] type from, everything you need to
[15:47] understand exactly how to fix this bug.
[15:50] It's amazingly useful. Okay. And clang
[15:52] clang's very clever. It will even edit
[15:54] your source code for you in order to
[15:56] rewrite this if you would like it to,
[15:57] but it waits for your request. It's not
[15:59] going to do that just just arbitrarily.
[16:02] So sometimes we make more annoying typos
[16:05] like this one, right? Because here we we
[16:06] haven't just made a typo. We've also
[16:08] forgotten about a namespace. There are a
[16:10] bunch of other things going on here. Who
[16:11] knows where this code came from? Well,
[16:13] Clang does. So, first Clang's going to
[16:16] realize that you named a namespace
[16:17] without the right qualifier. Okay, we
[16:19] can fix that. We'll go and add a
[16:20] qualifier to it. But remember, when we
[16:22] add something to it, when we correct a
[16:23] piece of code, we recover as if that
[16:26] were what was written. Which means that
[16:28] we go down and we find fooar as well,
[16:30] and we notice that you misspelled that.
[16:32] And so, we can correct that one just as
[16:33] easily. And we can keep going, and we
[16:35] can keep diagnosing errors in your
[16:37] source code without ever losing track of
[16:39] exactly what's going on. This is
[16:41] incredibly powerful. All right, this
[16:43] changes how you read diagnostics. This
[16:45] actually allows you to not send all of
[16:47] your error messages into head and just
[16:50] look at the first line because you're
[16:52] going to continue to get accurate and
[16:54] useful error messages throughout the
[16:56] file. Now, just because Andre uh
[16:59] mentioned Oscar Wild, I had to Oh, no.
[17:02] I've got more.
[17:05] No, where's Oscar Wild? We'll come back
[17:07] to that. Okay.
[17:09] I I think I I I tried his Oscar Wild
[17:11] diagnostic and and the book is very
[17:14] small.
[17:15] We'll have we'll have that later. Okay.
[17:17] So,
[17:19] now we're going to talk about something
[17:20] different. Now, we're going to talk
[17:21] about things that aren't ill-formed.
[17:23] This is this is valid C++ code, right?
[17:26] This code's correct as far as I can
[17:27] tell, right? Except there is certainly a
[17:30] bug in here. It's it's valid, but it
[17:33] doesn't behave the way we want. We've
[17:35] we've made a very simple typo in trying
[17:37] to use an old crusty C interface. This
[17:40] is why C interfaces are bad. They're
[17:42] easy to make these mistakes on.
[17:44] Fortunately, ah, there's Oscar Wild.
[17:47] Sorry, my slides are betraying me.
[17:49] Bear with me. So, Oscar Wild, Oscar
[17:52] Wild, let's let's make sure we cover
[17:53] Andre's Oscar Wild diagnostic because he
[17:55] had a he had a legitimate concern. What
[17:57] happens when you do template expansions
[18:01] and they don't simultaneously two
[18:03] template parameter pack expansions
[18:04] simultaneously and they don't line up?
[18:07] It's a very complex piece of code. If it
[18:09] goes wrong, you can see screens full of
[18:11] error messages. Or you can have the
[18:14] compiler tell you exactly what happened
[18:17] because you had two instead of three.
[18:24] I told you that this one was worth
[18:26] putting in. Sorry, I did this at the
[18:27] very last minute because of his his
[18:29] presentation.
[18:31] All right, so we're not done talking
[18:32] about diagnostics. Don't worry. All
[18:34] right, so when we when you write this
[18:37] bad C code, Clang is going to help you
[18:39] out here. Clang is going to give you a
[18:41] diagnostic that is not an error message.
[18:42] It's going to give you warnings. We love
[18:45] warnings in Clang. This is this is the
[18:47] bread and butter of Clang diagnostics
[18:49] are warnings. Warnings about incorrect
[18:52] code. Now, this is not invalid code.
[18:55] It's an important distinction. The C++
[18:56] standard allows you to write this code
[18:58] and it says exactly what it will do. But
[19:01] we can look at the code. We can look at
[19:03] the context of the code and we can be
[19:05] completely certain that you did not mean
[19:07] to write it.
[19:10] There is no chance that you meant to
[19:12] take size of S here because S has a
[19:15] completely different s. It's a pointer.
[19:18] All right, you're trying to set the
[19:20] contents of the pointer. It we need to
[19:22] actually understand the context for
[19:24] that. But we have that in the compiler
[19:26] and so we can tell you that you've used
[19:28] the wrong expression inside of the size
[19:30] of and we can tell you how to fix it.
[19:32] It's very very very useful. Um this this
[19:34] warning caught bugs in Chrome an open
[19:36] source web browser that you might be
[19:38] familiar with. It found bugs in all
[19:40] kinds of code that we've pointed it at
[19:41] because it's such an easy mistake to
[19:43] make and very often things happen to
[19:46] mostly work. All right. And that's the
[19:50] most frightening part is that oftentimes
[19:52] it will mostly work. It will initialize
[19:55] some of your data just not all of it.
[19:58] And it can be hard to figure out why.
[20:00] Okay.
[20:02] So then we can write more simple things,
[20:04] right? These are things that you might
[20:05] just write dayto-day. But unfortunately,
[20:07] even though this like clearly the user
[20:09] has messed up here, something has gone
[20:12] wrong. And yet it's valid C++. Nothing
[20:14] says we can't actually write this code.
[20:17] Nothing says this isn't valid. But Clang
[20:19] is very certain you didn't mean to. And
[20:20] so it tells you that you've got an
[20:22] expression that's completely useless and
[20:24] that you haven't even returned from the
[20:25] function. It's trying to help you out
[20:27] here by looking at the semantics of the
[20:29] code. Looking at the overall program
[20:30] meaning, right, we can keep going here.
[20:32] There are so many places where clearly
[20:34] the user did not mean to write this 100
[20:37] here. That's not okay. They can't just
[20:39] return that integer. There isn't even an
[20:41] integer there. They meant to write
[20:42] something else, right? And we can tell
[20:44] them that yes, this is out of bounds.
[20:46] actually you have to write you know 99
[20:48] there you can't write 100 there it's
[20:50] very very important
[20:52] we can even look at things that are a
[20:54] little bit more complex right we can
[20:56] look at things where clearly you know
[20:59] maybe the user forgets to ever
[21:00] initialize X some of the time clang can
[21:04] actually detect this right it's it's
[21:05] trivial to prove to a compiler although
[21:08] perhaps not to a human that in fact you
[21:10] never initialize X you never came close
[21:12] to initializing X we'll tell you about
[21:14] that we'll tell you where you used it
[21:15] we'll tell you how to, you know, make
[21:17] the compiler be quiet if you didn't
[21:18] care. I don't know why you wouldn't
[21:19] care, but okay. Right. We're trying to
[21:22] actually understand the program
[21:23] semantics and help you out. Now, this
[21:24] one is actually um one of my personal
[21:27] favorites because I wrote this code. All
[21:30] right. I wrote this code. I thought it
[21:32] was correct. Several other people looked
[21:34] at this code. They thought it was
[21:35] correct. I mean, not exactly this code,
[21:37] but equivalent code because it looks
[21:39] fine, right? We've been really careful
[21:40] to use a really big portable integer
[21:43] type. I know it's not standard. It's but
[21:45] you know we're getting that fixed. Yeah,
[21:47] it is now thankfully, right? But like we
[21:49] wrote a big type. We have space for this
[21:51] big number, right? We need 8 gigs of
[21:53] space here. What could possibly go
[21:55] wrong? Well, C++'s uh integer arithmetic
[21:59] rules are strange. They are not what you
[22:01] thought they were. And so this does
[22:03] something very surprising. This actually
[22:05] can, you know, order you a burrito if it
[22:07] likes.
[22:09] Um it can and and there's nothing you
[22:10] can do about it, right? because it like
[22:12] this has no meaning according to C++ and
[22:14] that's very unfortunate. It has a little
[22:16] bit of meaning but clang is going to
[22:17] tell you exactly what's going on here at
[22:20] with as much detail and precision as it
[22:22] can. It tells you what value you're
[22:23] trying to form, how many bits it needed,
[22:26] how many bits it had, where it all went
[22:28] arry. It doesn't matter what you're
[22:30] storing the value into. You have to
[22:32] actually compute the right value first.
[22:35] Okay, really important thing, right?
[22:37] This this this actually this is a real
[22:40] bug, right? All the things I'm showing
[22:42] you are real bugs that actually got into
[22:45] real production software. These are
[22:47] things that are important to find and
[22:48] it's very hard for a human being looking
[22:51] at I mean this looks fine.
[22:54] Okay, it's really hard to spot these
[22:56] errors through simple human review. So
[22:58] it's important to have some semantic
[23:00] checking here. So let's take it up
[23:02] another level. This is actually my
[23:04] favorite typo that I've ever seen in
[23:06] code because this code looks beautiful.
[23:09] What's possibly wrong with this?
[23:12] How many folks have spotted what's
[23:14] wrong? Got a few people who spotted
[23:16] what's wrong. This code lasted for
[23:18] years. Years. Okay. People didn't
[23:21] understand what was wrong with it. How
[23:24] many The question you have to ask
[23:25] yourself is how many variables have you
[23:28] like how many things have you declared
[23:29] here?
[23:30] Two.
[23:32] two.
[23:34] That's a function, not a ve, not a call.
[23:38] You've declared a function.
[23:40] There was a comma instead of a
[23:41] semicolon. Claim's not only going to
[23:43] tell you that you accidentally declared
[23:44] a function in a very strange way. It's
[23:46] going to see that you declared a
[23:47] function. There was a comma before it.
[23:49] And like that was actually a function we
[23:51] could have called. It'll go look. It'll
[23:53] see if it was a function we could have
[23:54] called. Make sure that we could call it.
[23:56] And then it'll say, "Oh, okay. You just
[23:58] meant to have a semicolon instead of a
[23:59] comma." We understand, right? It really,
[24:02] really works hard to clarify what the
[24:05] meaning of the source code is to tell
[24:07] you how to fix your source code. This is
[24:09] incredibly useful. There are very few
[24:11] programmers who are going to look at
[24:12] this and understand why this compiles
[24:15] cleanly and yet does not call bar. All
[24:19] right, that is very hard to understand.
[24:22] Fortunately, we have a compiler. It can
[24:23] help. Okay, so let's get to let's get to
[24:27] even more interesting things from my
[24:28] perspective. Again, I want to reiterate
[24:30] some of these may look trivial. I've
[24:31] made them fit onto a slide. These all
[24:34] came from real code, real bugs that were
[24:37] live in the wild in production. Okay, so
[24:40] you know, maybe we're trying to check on
[24:41] a customer's state. We're like updating
[24:43] state. We're trying to make sure we know
[24:44] whether or not to charge the customer
[24:47] money. This is important here, right?
[24:50] Remember the like burning things?
[24:52] Charging money is kind of burning things
[24:54] for companies, right? Like you don't
[24:55] want to do that the wrong way.
[24:57] Unfortunately, we have failed miserably
[25:00] at that goal because this doesn't
[25:03] actually set the flag to not charge
[25:06] people. No, no, no. It it sets a pointer
[25:09] to null.
[25:11] This surprise many engineers that I talk
[25:14] to. This is setting a point. Where where
[25:18] is null in this source code? I would
[25:20] have people challenge me. There's no
[25:22] null here.
[25:25] All right. This is one of the reasons
[25:26] why null pointer why all of these new
[25:29] features in C++ are important to use. We
[25:31] have to move away from pointers from
[25:33] from this very sloppy and easily errored
[25:37] code and clang is trying to help you
[25:38] catch it in existing code wherever it
[25:40] can. So then we get to um some other
[25:44] very very interesting pieces of code
[25:46] such as this. This is perhaps one of the
[25:49] most famous typos of all time. All
[25:51] right, this is so famous. Not only just
[25:52] clang like GCC diagnoses it too. There
[25:54] are a whole category of errors around
[25:56] this and and we've tried to make as many
[25:58] compilers as we can gang up on them
[26:00] because they're so common. I I have
[26:02] never seen a a a classroom of of
[26:06] students learning C++ where at least a
[26:10] quarter of them didn't make this mistake
[26:11] in the first month. All right, it's just
[26:13] too easy. But fortunately, we have
[26:15] compilers that can help us. They're
[26:16] going to tell you, okay, hold on, hold
[26:18] on. you're assigning in a condition
[26:21] that's not that likely. Let's let's
[26:23] actually take a closer look. We insist
[26:25] on a source transformation. Now, this
[26:27] gets to an interesting idea. What do you
[26:29] do when the compiler is warning you
[26:31] about code and you actually meant it.
[26:34] You you actually needed to do exactly
[26:36] what you wrote. And we can't take that
[26:38] expressivity of the language away. That
[26:40] would that would harm the language. That
[26:42] would harm programs. Instead, we add
[26:45] extra syntax to make it explicit to the
[26:47] compiler that this was intentional.
[26:49] Syntax that you would never just, you
[26:51] know, accidentally add to your program.
[26:54] If you wrap extra parentheses,
[26:56] completely pointless extra parentheses
[26:57] around this, the compiler's like, "Okay,
[26:59] okay, I'll leave you alone, right? You
[27:01] you get what you asked for, right?" is
[27:04] we have this ability to kind of silence
[27:06] warnings about, you know, questionable
[27:08] or bugprone
[27:10] patterns by source changes, compatible
[27:14] source changes. And this this is what's
[27:16] used whenever we get to warnings that
[27:18] are about bugprone patterns as opposed
[27:21] to certain bug patterns, right? Because
[27:24] some of these some of these there's no
[27:26] question. We've never seen a case where
[27:27] the user intended it that way. But this
[27:30] one this one is questionless. We need to
[27:31] have we need to have an escape hatch for
[27:33] users. Now, one of the interesting
[27:34] things GCC implements this the exact
[27:36] same way as clang does by the way. Um,
[27:39] but one of the interesting things we
[27:40] found was that we had failed to actually
[27:43] check for the reverse problem because
[27:45] sometimes people wrote the following and
[27:48] they did not mean to write the
[27:50] following. They were they had been
[27:51] indoctrinated by this pattern of adding
[27:53] extra parentheses to use an assignment
[27:55] and then they had made the inverse typo
[27:57] and we didn't have any way to tell them
[27:59] about that fact. And so now clang
[28:01] actually checks for that and it says
[28:02] like no no no no no no we've we've kind
[28:04] of blessed this particular syntax to
[28:06] mean assignment. We're going to enforce
[28:08] that. We're going to make sure you're
[28:09] actually doing it one way or the other.
[28:12] Now this was not the only place where we
[28:15] need kind of compatible source changes
[28:17] to clarify code. This is something that
[28:19] I actually found somewhat surprising.
[28:20] I've talked to a lot of people who
[28:21] worked with C++. They found it
[28:23] surprising as well. This code is
[28:26] incredibly prone to bugs and it's it's
[28:29] surprising that it is. People don't
[28:31] realize that these logical operations
[28:33] actually obey a particular algebra. They
[28:35] have precedence rules that follow from
[28:37] that algebra. That's not how they're
[28:39] thinking. And so they continually
[28:41] mistake what precedence relationships
[28:44] hold. And you get code written like this
[28:45] that was not intended to behave like
[28:47] this at all. And so we in Klein we
[28:50] actually have implemented a rule that we
[28:51] just don't allow you to do this. We warn
[28:53] on any implicit precedence relations
[28:56] between logical or and logical and. And
[28:58] we force you to explicitly group them so
[29:00] that we're certain what relations you
[29:02] want. And it gives you a very nice error
[29:04] message describing the problem and why
[29:06] you might be surprised by it, what the
[29:08] natural correction is. And you can go
[29:10] and add other parentheses as necessary.
[29:12] But we ask you to explicitly group them
[29:14] because it's so frequently bugprone. Now
[29:18] another one and this one was very
[29:19] surprising. This is another operator
[29:21] which has very clear precedence rules
[29:23] and yet no one understands. So any
[29:26] guesses on what this function returns?
[29:32] X always X never anything other than X.
[29:37] That was very surprising to a large
[29:40] number of engineers. The fact that that
[29:42] plus would actually kick in. And of
[29:44] course we have a lot of conversions to
[29:46] bool. It's very easy to compare things
[29:47] for true or false. And so the cascade of
[29:51] errors just propagated. This became very
[29:53] very pro uh prominent especially uh with
[29:56] with streaming output operators, right?
[29:58] When you're writing to see out or see
[30:00] error, those also group in a strange and
[30:02] surprising way. And they also have
[30:04] reasonably well- definfined semantics
[30:06] when they're integers on both sides. And
[30:08] they also usually produce either true or
[30:11] false. And that's very very surprising
[30:13] for users. And so this was very
[30:14] important for us to fix. Turned out it
[30:16] was very very common in code.
[30:19] And then we got to something that I I
[30:20] actually particularly like. Um this is
[30:22] this is kind of a a stunning typo. Um
[30:25] and you can also it's also hard because
[30:27] the indentation makes you really want it
[30:31] to be correct.
[30:33] Like like the seductiveness of Python's
[30:37] indentation just knows no bounds, right?
[30:39] And so you look at this and you know
[30:41] what this should do, but fortunately
[30:42] clang tells you what it actually does.
[30:44] It doesn't do what you want at all. All
[30:45] right? And then we can keep going here.
[30:47] There are so many cases here. You know,
[30:48] perhaps you're using a switch statement.
[30:50] One of the things we found was that
[30:51] switching over enums is very very
[30:53] bugprone. But we can fix this in the
[30:55] compilers by warning about it. We can
[30:57] actually come up with a really
[30:58] systematic way to handle every single
[31:01] case, right? Every single case because
[31:03] this one's missing Z. We don't handle Z
[31:05] here. What happens when Z comes in? We
[31:07] just, you know, we don't get anything
[31:09] useful. And this is particularly
[31:11] important for maintenance, right?
[31:12] Imagine you have an enum. Maybe it's a
[31:14] very widely used enum and you want to
[31:16] add something to it. How do you find
[31:19] every single user's code that needs to
[31:22] handle that new value? Right? Where do
[31:24] you even look? Right? We can actually
[31:27] come up with a way to write switches
[31:28] that are explicitly required to list
[31:30] everyone unless you have a default case.
[31:33] Right? Unless you have a default case,
[31:35] we're actually going to insist that you
[31:36] enumerate all of the enumerators. And
[31:39] it's very, very useful.
[31:47] It's already buggy, man. How much more
[31:49] do you want? Okay.
[31:51] Every single example of code I have is
[31:53] buggy in more than one way, apparently.
[31:56] So, all right. So, then we can get to to
[32:00] um I I think even more interesting
[32:02] things. And so, now we've got some some
[32:04] strange patterns here. We've got a
[32:06] constant. Maybe it wasn't a constant
[32:07] always. Now, it is a constant. But it
[32:09] turns out that we've ended up with you
[32:11] know dead code. Now that's okay. This
[32:13] happens sometimes. Uh the the common uh
[32:16] case is you know configuring configuring
[32:17] your compilation. You can imagine that
[32:19] you want to enable some things turn off
[32:21] other features uh in debug various other
[32:23] flag type values. You don't want to
[32:25] handle them special. You don't want to
[32:26] use more macros and more pre-processor
[32:29] tricks. So you just use the value and
[32:31] you write natural code. Unfortunately
[32:33] this these these flags often guard
[32:35] invalid code or surprising code or
[32:37] incorrect code. code that would only be
[32:39] correct if the value weren't true.
[32:41] Right? And so here we have such a case,
[32:44] right? When n is too large, we have an
[32:46] explicit return. We make sure that
[32:47] things don't break. So it's really
[32:49] really nice that when we look at this,
[32:51] right? We look at this, we actually see
[32:53] that 44 is past the bounds because it's
[32:55] a constant. Even though we seem to be
[32:57] handling things, we actually we're
[32:58] actually are trying to encode all the
[33:01] semantics here as closely as we can.
[33:03] Okay. All right. So, let's talk about
[33:06] static analysis because we're getting
[33:08] into static analysis. All these
[33:09] diagnostics, they're trying to reason
[33:10] about the meaning of your source code.
[33:12] We're getting more and more complex. We
[33:14] need to be honest about what we're
[33:15] doing. We're doing full-blown static
[33:16] analysis during your compilation to give
[33:18] you diagnostics about what is wrong with
[33:21] the behavior of your program. It's a key
[33:24] concept. The behavior of your program.
[33:26] And so, as we get more and more advanced
[33:28] in that, and clang isn't just your
[33:30] normal static analysis, right? Clang is
[33:31] is it's a little bit more powerful than
[33:33] that. that's going to actually try and
[33:34] fight you over your bad code and we're
[33:36] going to try and actually find ways to
[33:38] pin down even very subtle problems in
[33:41] your code. Right now this is a lot more
[33:43] code than I've been using. All right,
[33:44] this is when static analysis becomes
[33:46] important when we start to have complex
[33:47] structures in source code. Now I've
[33:49] deleted a lot of code from this. You can
[33:52] imagine this would be a very large
[33:53] routine be very complex routine but
[33:56] there are subtle problems here. There
[33:57] are problems that may not be easily
[33:59] spotted during code review. There are
[34:00] relationships there are preconditions.
[34:02] There are important constraints on the
[34:05] correct behavior of this code that are
[34:07] not being followed. We have mutual
[34:10] exclusion in play here. We're expecting
[34:12] concurrent access to this function. We
[34:14] have shared state in the form of a
[34:16] global variable, right? And we have some
[34:19] preconditions which are not being
[34:20] followed here. So there's at least one
[34:22] precondition that is trivially not being
[34:24] followed here. We are not correctly
[34:26] locking and unlocking our mutex. But
[34:28] what you don't see is that there are
[34:30] more preconditions that aren't even
[34:32] visible in the source code. Right now,
[34:33] in order to have compilers that are able
[34:35] to really diagnose the full range of
[34:38] subtle issues that come from concurrent
[34:40] programming, we need to give the
[34:42] compiler more information. We need to
[34:44] extend the language to give it specific
[34:46] domain domain information that will
[34:49] allow it to tell us about our code. And
[34:50] we can do this using C++ 11's
[34:53] generalized attributes to actually
[34:55] annotate the source code with critical
[34:57] information about its expected behavior.
[34:59] All right. Now, I'm filling in kind of a
[35:01] dummy mutex implementation up here at
[35:03] the top, but you would imagine that your
[35:04] standard library would come with a
[35:06] correct mutx implementation with a full
[35:07] set of annotations that completely
[35:09] describe its behavior. And what you're
[35:12] really concerned with are these
[35:14] annotations such as guarded by and
[35:16] exclusive locks required. All right,
[35:18] these are the important annotations.
[35:20] They're on your code and they're
[35:22] actually specifying the requirements of
[35:25] your code, not the requirements of other
[35:28] of the library. And that's a very very
[35:31] powerful thing because now when we look
[35:34] through this function, when we compile
[35:35] this function, clang can do very
[35:37] detailed analysis of whether or not you
[35:40] strictly follow in all cases the
[35:42] requirements you've laid out through
[35:44] annotations. All right. Now, of course,
[35:46] I have to I have to be fair and honest.
[35:48] I can't implement this today because
[35:50] generalized attributes are one of the
[35:51] things that's still about halfway in
[35:53] development in clang. Sorry. Sorry. I
[35:55] use the old attribute syntax. But I
[35:57] promise I promise it will work like
[35:59] this. This is what we want you to write.
[36:01] We never want you to see these. There
[36:02] are too many underscores. It's all bad.
[36:04] Okay. This is what we want you to write.
[36:06] And the result is going to look like
[36:08] this. The result is going to be clang
[36:09] going through and analyzing each step of
[36:11] your program and highlighting all of the
[36:13] missed preconditions here. Right? We are
[36:17] reading a variable that requires locking
[36:19] without holding the lock. We are calling
[36:20] a function that requires a lock without
[36:23] holding that lock. We fail to lock that
[36:26] mutex across a loop continuation. Okay.
[36:30] And we fail to unlock the mutex as we
[36:33] exit the function. All of this is
[36:35] clearly diagnosed by clang. going to try
[36:37] and very specifically highlight what has
[36:40] gone wrong in the desired semantics of
[36:42] the program. Right? Now this is complex.
[36:44] This is a very subtle thing. Right? And
[36:45] the importance of this is not for you
[36:48] know average code. The importance of
[36:49] this is for subtle code for concurrent
[36:51] code for code where it really really
[36:53] matters. Right? And the way we get this
[36:56] kind of powerful checking of our source
[36:57] code is by extending the language. Every
[36:59] time we can extend the language to
[37:01] codify the semantics, we can actually
[37:03] get improved checking from our tools and
[37:05] especially our compilers.
[37:07] All right,
[37:09] so let's talk about dynamic analysis
[37:12] briefly, right? Let's talk about dynamic
[37:15] analysis because static analysis is very
[37:16] powerful. It allows us to kind of set up
[37:18] invariants about our program and then
[37:21] enforce them and enforce them strictly
[37:22] regardless of what particular execution
[37:25] takes place. Those invariants are going
[37:26] to be held on all paths. But there's
[37:28] another type of analysis which is
[37:30] looking for something slightly
[37:31] different. It's looking for invariance
[37:33] that we may not be able to enforce
[37:35] pedantically throughout the entire code
[37:36] but invariance that may be very
[37:38] important. All right. And dynamic
[37:39] analysis is a fantastic tool for doing
[37:41] this. So you can imagine you've written
[37:43] some code some you know fairly poor code
[37:45] here and you've written two bugs in this
[37:48] code. Now one of these bugs we can spot
[37:50] statically. All right we can spot one of
[37:52] them statically very easily. Another co
[37:54] another bug it's a little trickier. We
[37:56] might be able to spot it statically, but
[37:58] we're going to have to actually like do
[37:59] loop trip count. We're actually going to
[38:00] have to look at that type of
[38:01] information. Make sure we understand how
[38:03] it works. Even if we do manage to spot
[38:05] that one statically, you can trivially
[38:07] modify this. You can move things around
[38:09] and very easily end up being unable to
[38:11] actually spot this bug statically. So,
[38:13] we would like to have actually kind of,
[38:16] you know, take a much closer look at the
[38:18] execution of the program and see if our
[38:20] invariance and if our basic correctness
[38:23] failed.
[38:24] And Val grind is kind of the canonical
[38:26] tool for this which is not related to
[38:28] clang at all but I'm setting the stage
[38:30] so bear with me. So Valrind is a
[38:31] canonical tool for this. You can see it
[38:33] will go through it will instrument the
[38:34] execution right and it will tell you
[38:36] about the things that have gone wrong.
[38:37] You've used something that is
[38:39] uninitialized and you've read past the
[38:42] end of the array. It's got a very good
[38:43] analysis of exactly what's going on. One
[38:45] of the downsides to Val grind is it
[38:46] actually catches a lot of other things
[38:47] that are irrelevant. It actually has a
[38:49] lot more text there but I tried to to
[38:51] distill what we're looking for. This is
[38:53] what we want. All right. But Valrind has
[38:56] some problems and they're very very
[38:58] deep-seated problems. The first problem
[39:01] is that Valrind cannot possibly
[39:03] instrument everything. In fact, if we
[39:05] change this code very trivially, we
[39:07] actually simplify the code here. Right?
[39:09] This code is now less complex. And yet,
[39:12] when we run it with Val Grind, we catch
[39:14] fewer bugs.
[39:16] All right? This is a very big problem
[39:18] because usually we're telling people to
[39:21] simplify their code, right? You don't
[39:23] you don't add complexity to your code to
[39:24] catch bugs. You simplify it to catch
[39:26] bugs. But as we simplify our code,
[39:28] Valrine sees us to be able to analyze it
[39:30] effectively. And this problem stems from
[39:32] an underlying issue with Valrind and
[39:34] that it's doing dynamic instrumentation
[39:36] of your of your executable and it's
[39:38] doing so through through very expensive
[39:41] means, right? because it's running. It's
[39:44] it's actually simulating the execution
[39:46] of your of your program and it's very
[39:47] very costly. In order for that to be
[39:50] even tractable, it has to give up on
[39:52] certain things, right? It has to
[39:54] actually not check certain behaviors.
[39:57] One of the things that it does not check
[39:59] are are behaviors that are expected to
[40:01] be very fast, such as putting things on
[40:04] the stack. And so those don't get
[40:06] instrumented by valrind. And as a
[40:08] consequence, bugs don't get caught. But
[40:10] the whole point of C++ is that you can
[40:12] actually make things fast and simple at
[40:15] the same time. And so of course we want
[40:17] to write the code this way and we would
[40:19] expect our tool to deal with it and
[40:20] diagnose the bug. Val grind is not going
[40:22] to be able to do that. Even with these
[40:24] optimizations in place, Val Grind is
[40:25] simply too slow. All right, Val Grind
[40:27] slows down the execution of your program
[40:29] by 20x, sometimes 30x. That's often an
[40:32] unacceptable slowdown. You may not even
[40:34] finish running your program to observe
[40:36] the bug and get a report for the tool.
[40:38] We need to do better than that. There's
[40:41] a very kind fundamental way that we can
[40:42] do better than that and that is to
[40:44] actually do the instrumentation to do
[40:46] the anal the dynamic analysis through
[40:48] clang itself. Because clang is not just
[40:50] a front end. It doesn't just analyze
[40:52] code. It actually generates code and in
[40:54] generating code clang can generate
[40:57] exactly what we need to dynamically
[40:59] check each of these invariants. And by
[41:01] doing it when we generate the code we
[41:03] can be much much more efficient and we
[41:05] can be more accurate. And this is
[41:06] something that's ongoing in development
[41:08] as part of clang and LVN. It's called
[41:09] address sanitizer which replaces some
[41:11] parts of val grind and and the follow on
[41:14] to that is actually called thread
[41:15] sanitizer which does data race
[41:17] detection. Now in the interest of time
[41:19] I'm not going to go into a lot of
[41:20] details. Also sadly I can't I couldn't
[41:22] get the examples for thread sanitizer to
[41:24] work because all of the libraries that
[41:26] work with uh thread sanitizer don't
[41:28] support threads in C++ 11 and I was not
[41:31] going to write pix threads for this
[41:33] talk. that that would just be too I
[41:35] couldn't I couldn't I couldn't get up
[41:36] here and actually project Pix threads at
[41:38] you guys like I I would get run off the
[41:40] stage rightfully so
[41:44] okay but we can do some things we can do
[41:48] some things with clang today and I at
[41:50] least want to mention those because this
[41:51] is this is the way in which we want to
[41:54] do dynamic analysis so here we have a
[41:56] bug that's very similar to a bug we saw
[41:57] earlier where we're actually shifting
[41:59] too far right but this one we can't
[42:01] catch statically there's no there's no
[42:03] static analysis is going to work. These
[42:05] two functions might be in completely
[42:06] different files. One of them might be
[42:08] shipped to you from a library vendor.
[42:10] There's no way you're going to easily
[42:11] catch this, right? We need something
[42:13] more powerful. Well, we can actually ask
[42:15] Clang to build this with dynamic
[42:17] instrumentation enabled to check for
[42:18] this undefined behavior. Clang has a
[42:20] mode called catch undefined behavior. It
[42:22] will actually add some limited
[42:24] instrumentation to the binary. Very,
[42:26] very lightweight, very low overhead. And
[42:29] when you run it, you'll actually get a
[42:30] crash rather than undefined results.
[42:33] Right? It'll crash at the exact point at
[42:35] which undefined behavior would have
[42:36] happened. All right? This is again this
[42:38] is something we're trying to develop in
[42:40] clay. This is the future we think. Um
[42:42] and so I don't I don't want to give the
[42:43] impression that this is this work is
[42:44] done. There's a tremendous amount of
[42:46] effort to be done in the form of dynamic
[42:49] analysis aided by compilers because
[42:52] that's what will enable dynamic analysis
[42:54] to be fast and accurate enough to be
[42:56] usable by everyday engineers all the
[42:58] time. For reference, you can take
[43:00] address sanitizer, you can use it to
[43:02] secure Chrome, a web browser. You can
[43:06] load it. You can browse the web
[43:07] interactively. You don't even notice the
[43:09] slowdown. All right? That's the kind of
[43:11] dynamic instrumentation we want because
[43:13] that's what we can use to catch real
[43:15] bugs in in pervasive situations. Okay.
[43:20] So, let's talk a little bit about C++.
[43:23] We've been talking about these tools.
[43:24] We've been talking about Clang, what we
[43:25] can build with Clang, but I want to
[43:27] bring things back to C++. And in
[43:28] particular, I want to bring things back
[43:29] to C++ 11 because that's what we're all
[43:32] here to talk about. And C++ 11, I want
[43:34] to say, is making things much much
[43:36] better. I had a bunch of slides in this
[43:38] talk. I was going to like show all kinds
[43:40] of crazy slides about all the ways that
[43:41] C++ 11 makes your code less prone to the
[43:44] types of errors I've shown, but
[43:46] unfortunately or fortunately, Garnet and
[43:49] and Herb have done a much better job of
[43:51] showcasing all of the beautiful things
[43:53] you can now do in C++. I think they
[43:55] literally had a slide for every single
[43:57] example I had up there. Um, so you guys
[44:00] know what you can do now. You can you
[44:02] have safe memory uh management. You have
[44:04] safe threads. You have safe concurrency
[44:06] primitives that you can use and you can
[44:07] rely on. And that is what's going to
[44:09] make your code correct and make your
[44:10] code predictable. But we're not done
[44:13] yet. All right. There is more work to be
[44:15] done with C++ 11 to make it really safe
[44:17] to help defend about it. Uh Herb Center
[44:19] started to hint at some of this. Um and
[44:21] you're going to see some similarities
[44:23] here because I worked closely with
[44:24] Richard Smith who gave him one of these
[44:25] examples. So you might have written this
[44:27] code initially when you're writing in
[44:29] C++ 11. You might have written this code
[44:31] and it would work as you would expect.
[44:32] It would do what you would want it to
[44:34] do. But you would have, you know,
[44:35] listened to his talk and you would have
[44:36] heard that you should use auto. There's
[44:37] no reason to be declaring a type here.
[44:39] But unfortunately, by trivially adding
[44:41] auto to this code, you actually make it
[44:43] do something completely insane. All
[44:45] right? Because in fact, when you run
[44:47] this code, it's going to actually
[44:48] assert. And and again, you'll notice I
[44:50] was not able to use clang here. I did
[44:51] want to bring this back to C++ 11, but
[44:53] uh remember generalized initializer
[44:55] lists. Yeah, that's one of the things
[44:56] it's not done yet. I'm sorry. We're
[44:58] working on we're working on as fast as
[44:59] we can, but that one's not done yet. So,
[45:01] so this is an interesting thing. We
[45:03] don't have a warning here, right? We
[45:05] don't have any diagnostics here. We're
[45:06] not catching any of this. But we could
[45:08] we truly could. We could actually look
[45:10] at how this variable is used. We could
[45:12] look at what type it actually has. And
[45:14] we can see that using auto embrace
[45:16] initialization for this is probably
[45:18] suspect. It's doing something really
[45:20] strange here. It's very unlikely the
[45:22] user actually wanted to create a
[45:23] variable with the, you know, standard
[45:25] initializer list type. Especially when
[45:27] we go and look at how it's used later
[45:29] on. It's very, very unlikely that that
[45:31] was the intent of the programmer. We can
[45:33] detect this and we can warn about it. We
[45:35] need to start building up a body of
[45:36] warnings in clang targeted at C++ 11.
[45:40] That's the next big thing for making C++
[45:42] 11 safe. The standards committee has
[45:44] done their job. They've actually tried
[45:46] to craft a language that is as safe as
[45:48] possible, that is predictable as
[45:49] possible. But as we gain field
[45:51] experience, there are two things that
[45:53] are going to happen here. one, we're
[45:54] going to go to the language and we're
[45:55] going to improve it and we're going to
[45:56] fix the bugs in the language. And
[45:58] simultaneously, we need to go to the
[46:00] impleers and we need to ask them to
[46:02] catch these pitfalls for us to tr to
[46:04] make sure that they are actually looking
[46:06] after us and they're looking after the
[46:08] other engineers learning it. And it's
[46:09] very important. It's very important that
[46:11] as implementers of C++ 11 that we race
[46:15] ahead that we race ahead and find these
[46:17] pitfalls early and get warnings in place
[46:20] so that as people are learning C++ 11,
[46:22] as we are trying to get adoption of the
[46:24] language, people are not scared away by
[46:26] pitfalls that are easily handled and far
[46:28] more minor than the pitfalls of the
[46:30] slides you've just seen. Right? These
[46:33] pitfalls are small and we can fix them
[46:36] but we need to do it fast so that we can
[46:38] get adoption. Okay. So there are a
[46:40] couple of other interesting pitfalls I
[46:41] want to look at. One of them is regards
[46:43] to decal type. Decal type is really
[46:45] really tempting because you don't have
[46:47] to write type names. You shouldn't have
[46:49] to write type names. Unfortunately there
[46:52] are some expressions that don't work
[46:54] correctly with decal type when you use
[46:56] it in this way. It's not doing what you
[46:59] mean for it to do because this
[47:00] expression does not necessarily have the
[47:02] type you expect. This expression can
[47:04] have the type of a template. That
[47:06] template might be something very
[47:07] surprising. It might be an RV value
[47:09] reference. It might be something else.
[47:10] You don't want to blindly use decal type
[47:13] for certain scenarios because you can't
[47:15] predict what it will do. Okay.
[47:18] Similarly, we have cases where we have
[47:20] something that seems very nice
[47:22] originally but actually is a trap.
[47:24] Right? We can use move semantics to
[47:26] avoid wasted copies, but we have to be
[47:29] careful because references are just
[47:33] references and we can move away local
[47:35] variables. We can leave dangling
[47:37] pointers. It's very, very easy to make
[47:39] mistakes like this. We need to actually
[47:41] implement warnings for the new ways that
[47:43] these happen with C++ 11. They should be
[47:45] less often, but we still need the
[47:47] compiler's help here because humans
[47:49] aren't going to spot the error, right?
[47:50] We have to have the tools to help us.
[47:53] Finally, and this one, this one is
[47:55] actually the one that scares me. And
[47:56] this one is one that will never happen
[47:58] like daytoday. This one's going to
[48:00] happen once in a blue moon. These are
[48:02] these are the pitfalls that you can't
[48:04] find all of in the language. You simply
[48:06] must catch them in the implementation
[48:09] and tell the user what has happened.
[48:11] What do you think is printed by this
[48:14] program?
[48:16] Any guesses? What do you think? No. Come
[48:18] on. What do you think is printed by the
[48:19] program? Oh, let me ask a different
[48:21] question. What do you want to be printed
[48:22] by this program?
[48:23] X.
[48:24] X. We all want X to be printed by this
[48:26] program. What do you think is actually
[48:27] printed by this program? Well, actually,
[48:28] hold on. It turns out that it is
[48:30] printed. X is printed
[48:32] in the old standard.
[48:35] C++ 11 changed the semantics of this
[48:38] program. Suddenly,
[48:40] this is no longer going to form an X
[48:44] object. It's no longer going to call
[48:45] that function. It's going to pick an
[48:46] entirely different overload. This
[48:48] expression at the bottom is a null
[48:51] pointer constant.
[48:53] This is a surprising and unfortunate
[48:55] interaction between generalized constant
[48:57] expressions, default initialization
[48:59] rules, and the fact that we must remain
[49:02] compatible with C++ 98. We have to
[49:05] support the old forms of null pointer
[49:08] constants. When you combine all three of
[49:10] these together, you get this code. Do we
[49:12] expect programmers to write this? No. No
[49:15] one's going to just like write this
[49:16] every day. This is not going to be a
[49:17] pervasive problem, but when someone
[49:20] accidentally writes this by some strange
[49:22] happenstance, they're going to lose
[49:24] hours of their day trying to figure out
[49:26] what on earth took place, and they're
[49:28] not going to understand it. We need to
[49:29] catch these types of pitfalls with
[49:31] tooling that can actually tell
[49:33] programmers what has happened and
[49:34] correct it for them. And it's really,
[49:36] really important. This may seem like an
[49:37] edge case, but the time lost when it
[49:39] happens is massive, right? And it shakes
[49:41] confidence in the language. It makes
[49:42] people distrust C++. And there's no
[49:45] reason to do that here, right? This is
[49:46] this is the the ultimate of edge cases.
[49:51] So let's talk about making C++ safer. I
[49:54] would really like to talk about that. I
[49:55] I I hope you have some good questions
[49:57] for me. And I also want to mention one
[49:59] thing and this is a shameless shameless
[50:00] plug. Clang is an open source project.
[50:03] We need patches. And I don't mean
[50:05] patches welcome in the snarky way that a
[50:07] lot of people like to say like haha
[50:09] patch is welcome. We're not doing your
[50:10] work. We're not going to actually
[50:11] maintain this. No, no, no, no, no. I
[50:13] mean patches are really, really welcome.
[50:15] Clang is unique in that it's an open
[50:17] source compiler that's written in C++
[50:20] that's a very friendly codebase and has
[50:22] a very friendly community. Please come
[50:25] submit patches to it. We need more
[50:27] people contributing to make this better.
[50:29] Okay. And so if you have any questions
[50:31] about how to contribute to clang, please
[50:32] come and ask me. Um and now I think we
[50:34] have a little bit of time for questions
[50:36] and I think someone someone would like
[50:37] the very first question.
[50:38] Could you could you walk us through the
[50:40] previous example?
[50:42] Okay. I I didn't quite understand your
[50:45] explanation.
[50:46] I didn't understand it either. Um
[50:48] Richard Smith and I worked on this one
[50:50] for a day or two to make sure we like we
[50:52] think we understand it. We may still be
[50:54] wrong. All right. Just like Howard said,
[50:58] this is a new language. We are all
[51:00] learning about these interactions. So
[51:02] the belief is that S picks up a default
[51:05] um a default constructor here, right?
[51:07] And that default constructor is a valid
[51:10] constant expression because this is an
[51:11] aggregate, right? There's nothing that
[51:12] would make this not viable for use in
[51:14] inside of a constant expression.
[51:17] All right, once we do that, we know what
[51:19] the meaning of that default constructor
[51:20] is. The meaning of the default
[51:21] constructor for S is that it zero
[51:23] initializes in inside of a constant
[51:25] expression. All right, and this is
[51:27] important. Now, the next key piece here
[51:31] is that when we construct a temporary s
[51:32] down here, that actually forms a
[51:34] constant expression because we might be
[51:36] trying to call a constant expression
[51:37] function.
[51:39] And then when we pick n out of it,
[51:41] that's still a constant expression
[51:42] because we we've made sure that s is
[51:44] nice and simple and fits all of those
[51:45] rules. Again, we might be trying to call
[51:47] a constant expression function. Yes,
[51:48] this is in main, but maybe it's not in
[51:50] main. Maybe it's even in a template
[51:52] function. We might be trying to
[51:53] implement a constant expression here.
[51:54] And so we follow those rules all the way
[51:57] down. Now, at the end of the rabbit
[51:58] hole, we end up with an integer whose
[52:00] constant value is zero. And there's no
[52:03] distinguish between this version of an
[52:06] integer value zero and the literal zero
[52:08] character written in the source code.
[52:10] And so we must assume that it could be a
[52:12] null pointer constant. And we call f.
[52:14] Now why didn't this form a null pointer
[52:16] in C++ 98? Well, we didn't have
[52:18] generalized constant expressions. We
[52:20] didn't have a way to say that we might
[52:22] be calling a constant expression. And so
[52:24] we knew very clearly that default
[52:26] constructing s here had nothing to do
[52:28] with constant expressions, right? That
[52:30] was not even in the picture. And so we
[52:32] also knew that n coming out of it wasn't
[52:34] going to be a constant expression. That
[52:35] wasn't in the picture. It was just the
[52:37] value zero with a very clear type. And
[52:39] because of that, it was actually able to
[52:41] hit the implicit um conversion and
[52:43] actually form X.
[52:45] I'm so sorry.
[52:48] I I have a question. It seems like
[52:50] besides having a warning about this,
[52:52] which is great. Thank you very much. Um
[52:54] it seems also like we ought to be able
[52:56] to fix this in the standard. It seems
[52:58] like a bug. Have you submitted a defect
[52:59] report? uh as I think Richard came up
[53:01] with it Monday. No, we have not
[53:03] submitted a defect report. Um we will uh
[53:07] it isn't clear how to fix this though
[53:08] because it it maybe you have ideas but
[53:11] it it seems tricky. So we'll see.
[53:16] I suggest
[53:18] I have a question for you here. All of
[53:20] the um stuff you were mentioning up here
[53:22] sounds absolutely wonderful and all I
[53:25] can think of is how much it's going to
[53:26] increase my compile times and how it's
[53:28] especially on the link times. Is there
[53:30] anything we can do at least especially
[53:32] on the link time is to make this go
[53:35] better. Um template link times are one
[53:37] of the reasons that that lots of people
[53:39] don't go that way and as a compiler
[53:42] person that falls into your court. Okay.
[53:44] Thank you.
[53:45] Uh okay. Okay, so the question is how
[53:47] how can we how can we ensure that
[53:48] compile time doesn't decrease? Well, I
[53:50] have a couple of things to say to that
[53:51] one. The one one is I'm very sorry I
[53:53] couldn't actually put this in the
[53:54] slides. Um, a co-orker of mine managed
[53:56] to actually get benchmarks of Clang's
[53:58] compile time last night and get the me
[54:02] these really nice graphs and I just did
[54:03] not have time to put them into the
[54:04] slides. I may try and squeeze them in.
[54:06] But but let me say that Clang is really
[54:09] really fast for slow C++ code. for
[54:12] heavily templated C++ code. Clang is
[54:14] about twice as fast as GCC at compiling
[54:17] your code. All right, and that's a big
[54:19] improvement. 2x is a big number in in
[54:21] compile time performance because people
[54:22] have been looking at this for a while.
[54:24] Link time performance is something
[54:25] different. I I think there are a lot of
[54:27] interesting proposals of how we might
[54:28] solve the link time performance issue.
[54:30] Um, some of the things that I've I' I've
[54:33] seen talked about recently uh include
[54:34] modules and using modules to actually do
[54:36] more of the linking work ahead of time
[54:38] to make dynamic libraries more rich
[54:40] things that we can kind of link once and
[54:42] ship to your system and you simply refer
[54:44] to. I think that that has that has great
[54:46] promise. I also think that our current
[54:48] linkers are not slow because of how much
[54:52] work we are asking them to do.
[54:55] I I think that our current linkers are
[54:57] slow because we haven't yet fully
[54:59] studied the problem of how to link
[55:01] binaries efficiently and we haven't yet
[55:03] done that study in a in a radical enough
[55:06] mindset. We often get constrained when
[55:08] we're looking at linking problems by
[55:10] trying to support um 20 to 40 years of
[55:13] history. Right? We sometimes get
[55:14] constrained by trying to use the exact
[55:16] same file format and specification that
[55:18] was optimal in 1975. And it's not clear
[55:21] that that remains the case. And so I
[55:23] think that there are a lot of really
[55:24] interesting ways to actually make links
[55:26] dramatically faster. And I mean I mean
[55:28] you know orders of magnitude faster
[55:30] simply by reexamining the problem from
[55:32] the ground up and being willing to kind
[55:34] of do a more a more fresh start
[55:36] approach. Um there's actually a project
[55:39] that's part of LDM uh called that is
[55:41] trying to write a new linker. I don't
[55:43] know if they're really going to aim to
[55:44] do that the first time around, but I do
[55:46] have some hope we may get some radical
[55:48] ideas that look to improve this.
[55:52] Go ahead. Uh just a followup. Um it
[55:55] would have been nice if people would use
[55:57] null pointer instead of uh zero and null
[56:01] and that would have eliminated that
[56:03] problem. But I have a suggestion.
[56:06] If people write a zero uh constant
[56:11] that's not actually called zero or um I
[56:15] mean one minus one or that one give a
[56:17] warning. Um, so that's actually the
[56:19] warning that Richard Smith implemented
[56:21] in clang because of finding this. Um,
[56:24] and we looked at a large body of code
[56:26] and it was actually very surprising how
[56:28] frequently this warning fired. Um, we
[56:31] found a large number of places where
[56:34] people were doing this. Some of them
[56:36] correctly and dubiously doing this.
[56:38] Other places we found bugs just rife
[56:40] with bugs. But there actually is correct
[56:42] code that appears to use this to um
[56:45] choose nonp preppprocessor nonliteral
[56:48] names for null. Um we found a lot of C++
[56:52] 98 code that used a constant integer k
[56:54] null because it wasn't a pre-processor
[56:56] macro. Dubious, but I don't know how how
[56:59] best to fix it. It's likely this warning
[57:01] will end up making it in.
[57:04] You mentioned you mentioned that uh
[57:07] clang doesn't work on Windows. Could you
[57:09] elaborate on that? I I would love to
[57:11] elaborate on that especially with this
[57:12] audience.
[57:14] Um so so clank faces some big challenges
[57:17] with Windows. One of them is uh quite
[57:19] frankly uh visual C++ is a really good
[57:22] compiler. We that slide or the several
[57:25] slides of motivating examples that I
[57:27] gave at the beginning that doesn't apply
[57:29] to most Windows development. And so it's
[57:31] it's consequentially a lot harder to
[57:33] build a big movement to go and undertake
[57:35] the very very significant effort of
[57:38] building Windows support into client.
[57:40] And that that's I think actually the
[57:41] primary reason why. A secondary reason
[57:43] why is that as a consequence of the
[57:45] first the existing client community
[57:48] includes very very few people with any
[57:51] knowledge or expertise about Windows and
[57:53] so we're woefully unable to support it
[57:54] and unable to build new features in it.
[57:57] There are a small number of people that
[57:58] are working on this because of things
[58:00] like diagnostics and static analysis and
[58:02] some of the potential uh benefits of
[58:03] that. But there are a small number of
[58:05] people um facing a very very large
[58:08] project and the rest of the community
[58:10] can't even help them very effectively
[58:11] because it's out of our domain. And so
[58:13] the thing we need to make Windows
[58:16] support and client a reality are two
[58:18] things, right? And they're the two
[58:19] things that every open source project
[58:20] I've ever in the world looked at needs.
[58:23] We need patches and we need
[58:24] infrastructure because there's no one
[58:27] company that's providing the
[58:28] infrastructure for an open source
[58:29] project. And so you need to get it from
[58:31] as many different places as possible.
[58:32] And it turns out that the two companies
[58:33] that try to provide infrastructure are
[58:35] really bad at providing Windows
[58:37] infrastructure. We don't know what we're
[58:38] doing there and we do a bad job of it.
[58:41] Right? And simultaneously all the people
[58:43] contributing patches don't know what
[58:45] they're doing with Windows patches and
[58:46] they don't do a great job of it. Right?
[58:48] I can't even review Windows patches
[58:50] coming into Clang because I don't know
[58:51] enough about Windows to assess whether
[58:53] they're correct. We need people and we
[58:56] need infrastructure. If you'd like to
[58:57] help out, we'd love to have you.
[58:59] Excellent.
[59:02] Okay. Uh you you said that you have no
[59:04] errors, warnings, are you envision you
[59:06] will have suggestions? So for example,
[59:08] client can suggest that you can use
[59:10] initializer list here or you can
[59:12] substitute this for with algorithm or
[59:14] you can use auto here or something like
[59:16] that. So it is easier for people to
[59:17] transition to some best practices.
[59:20] Absolutely. Um we definitely want to
[59:23] have suggestions. So if you'll note
[59:24] almost all the diagnostics we show that
[59:26] when appropriate we have these notes
[59:28] attached to them that give candidate
[59:31] fixes. Now there's something you have to
[59:32] understand though about suggesting
[59:34] fixes. Um, Clang will only actually
[59:37] attach a suggested fix fix to the
[59:40] diagnostic itself when the diagnostic is
[59:42] an error and when it can actually
[59:44] recover from that error by parsing as if
[59:47] the user wrote what it suggested. Unless
[59:50] we do that, you don't get a suggestion.
[59:53] And the reason is that it's very very
[59:55] easy to to to cause suggestions to take
[59:59] the entire program off the rails.
[60:02] There's been a lot of research here.
[60:03] It's easy to let to run away with
[60:05] suggestions and so we have a very high
[60:06] bar for suggesting fixes. Warnings are
[60:09] different because warnings are not
[60:10] invalid code. We cannot recover parse.
[60:14] We can't reject the program. And so all
[60:16] of our suggested fixes for warnings are
[60:18] attached to notes as options for the
[60:21] user to take, right? But we absolutely
[60:23] need to have options for the user to
[60:24] take for all the C++ 11 warnings we come
[60:26] up with. And if we don't if there's not
[60:28] a best practice that we can suggest
[60:30] immediately when we spot one of these
[60:31] issues then we have an even bigger
[60:33] problem right we have we can suggest you
[60:35] know null pointer here and that's really
[60:37] important and we can suggest other
[60:38] things that are really important here we
[60:40] need to make sure that we have
[60:40] suggestions that are actually best
[60:42] practices and well understood.
[60:45] Excellent. Thank you Chandler. Big hand
[60:46] for Chandler.
[60:48] Awesome.