Carbon is a pragmatic attempt to fix C++ from within, prioritizing seamless migration over the radical "rewrite everything" approach. It remains to be seen if this middle ground can truly solve memory safety without inheriting the very complexity it aims to escape.
Deep Dive
Prerequisite Knowledge
- No data available.
Where to go next
- No data available.
Deep Dive
Carbon: graduating from the experiment - Chandler Carruth - NDC Toronto 2026
Added:So, I figured we should go ahead and get started.
So, I'm general here to talk about carbon. Um, I'm actually pretty excited to to share all of this stuff with you about carbon. And folks may not remember carbon. It was a few years ago when we were actually here in Toronto, uh, four years ago to actually announce that working on this project. Uh, don't ask me why YouTube wants to not have this thumbnail.
>> I I tried fixing this four times before and I don't understand, >> but we were here and it was really, really fun. We really appreciate all the C++ North. We're really excited to be back here. So, it was really nice to do the announcement, but it has been four years. I wanted to try and give folks a small update about what all we've been up to. Um, just as a reminder, if folks don't remember, right, the reason we're doing carbon is because C++ is not meeting the demands of our users, right?
We've got 40 plus years of technical debt and growing. Uh, the backwards compatibility is almost always prioritized over making like real and substantial improvements. It has a complex, slow, inconsistent, and frankly toxic evolution process uh in the standards committee. Uh I'm not going to not going to pull the punches here. This is it is not an effective process for evolving a language. And the result is unsurprising. All of the modern programming languages are running away and C++ is getting left behind. The gap is growing and it's already really painful. Uh we we are struggling to move away from textual inclusion. The compile times are unbearable. uh macros are still pervasive and they're still referencing the C standard as much as C++. There's no type check generic system. There's no package manager and of course there's also no memory safety.
Uh memory safety is becoming increasingly painful uh for everyone in the industry and C++ has made no material and significant progress on addressing this.
This isn't a new problem, but it is fundamentally an existential risk for C++ now. And so we wanted to try and do something. The obvious thing to do of course is to just go and evolve C++ to address these problems. Uh a bunch of really wonderful people have tried to do that for decades and unfortunately uh it's not going well. Right? The toxic and ineffective evolution process has stymied all of our efforts to really make these kinds of material improvements. And that's where carbon was born. was like, well, if we cannot improve C++ directly, what else can we do? We have all the C++ in the world. It is incredibly important. We wondered, could we build an incremental successor language for C++? something that would give us this nice incremental path to evolve and migrate off C++ to add memory safety to existing software to improve compile times hopefully by 10x or more uh to generate smaller and faster binaries and to still prioritize the most brownfield C++ systems right places where we have large complex existing C++ code bases so that's where we're working with carbon but I want to really share with you how it's going we're trying to do this through some updates The open source project has been incredibly busy since we announced it four years ago. Over 5,000 PRs, over 90 contributors. Uh we have 179 merged proposals, formal proposals for how to evolve carbon. Uh and 159 closed leads issues. These are questions about what we want to do in a particularly challenging aspect of the language design, right? And so the open source project is really working. The leadership and governance model is working well. we're actually able to evolve things and do so openly and transparently. We have weekly summaries of everything happening in the project called Last Week in Carbon. We've been posting those every week for the past year. Uh we also have 12 editions of our bimonthly newsletter called the Carbon Copy. It's like the best naming job of anything we've done. Uh and we have an active Discord server where all of our discussions take place. uh if you want to join, if you really like programming language design and you're you have time and you're interested, this is a super fun community. It's been really really enjoyable to be uh part of it and to kind of work on these things.
But when we started this off, we also said that we were just getting started.
We came we we decided to make carbon public extraordinarily early. Nothing worked. We didn't even have a compiler.
It was it was as early as we could conceivably open it up and get more people involved because we wanted to build carbon in the open. The consequence of that was that a lot was going to change. We knew that a lot was going to change. And let me tell you, it has changed. And so, let's let's kind of reset all how carbon works. Let's look at what carbon actually looks like today. So, here's a bunch of carbon code, right? Um, pay no attention to the suspiciously blank sections here, but we have some carbon code. It's going to be pretty standard stuff, right? We have a class called vending machine. got a function that's going to create uh vending machines probably. Uh it takes an input parameter called price, right?
Which is a 32-bit integer. It returns self. Self is an alias to the class that contains it. And and this is this is a factory function, right? This is going to take the place of your constructor in C++.
We have fancy strruct literal values and the this is actually a really nice way to initialize things. It kind of takes the place of list initialization with designators in C++. We have a slightly more typebased and kind of definitive way of modeling that but it lets us achieve the same nice initialization of our fields.
We also have another function dispense item but this isn't just a function.
It's it's actually a method uh because it accepts the object parameter and that's explicit in C++ instead of implicit uh sorry explicit in carbon instead of implicit in C++. This isn't just any object parameter. This is a reference parameter. So one of the biggest changes we made to carbon was we added references to the language and so we have the ability to model reference parameters. That means that when we take self by reference, we can come down and mutate our count. Right? All of that works. We also have methods like print profit which don't take a reference.
They just take this input self parameter. And you can think of these input parameters like self and price.
Here is a const reference in C++. It's just the kind of default we want you to use. We make it the shortest possible thing you can use. Here we have private things. We actually have access control and that kind of highlights something interesting. We didn't say public anywhere. Uh carbon takes an interesting approach that we was kind of inspired by cotlin. Public is the default when you're writing an exported API file. When you're writing something that you want people to import and call as some kind of API that's mostly public and in particular the parts that people are reading are the public parts. We want those to be as tur and crisp as possible.
Right? And so all of these functions were public. They're part of the API. We didn't have to say anything. The readers don't have to read any extra keywords.
We don't have public static, void, final, main, whatever for Java, right?
We we don't have that problem. And then there is a keyword. When we have something that happens to be textually in the API, but isn't actually part of the API, we say that that's private. And this is actually a great use of a keyword. So if if I'm not caring about the implementation, I can just stop reading as soon as I see that private skip. Uh so this this actually works out really nicely. It's it seemed counterintuitive at first. The more we've done with this, it works really nicely.
And the private thing here is of course a a member variable. We have the price, right? Nothing nothing too surprising here. Okay, so folks can probably understand how all this works. Let's add some more carbon code. This is the same file just just two columns so that it'll fit on a screen. Um, again, please pay no attention to the suspiciously blank areas over here. That'll make sense later. So, we also have a run function down here. And this is a little bit interesting. We didn't actually say what package we were in in Carbon. One of the things we've added is a default package.
When you don't say that I am I am writing some particular package of code, a library in some particular package of code, you're not a library. And what does that mean? Well, that means that you're a program. We call that package the main package. And you need a function that's going to actually run your program, right? This isn't an API anymore. This is just the actual program. So we can have a run function.
We can declare a variable for a vending machine. We can call a method to get some items price out of it. We can print that, right? We have we can call methods. All the kind of basics of a language are there. Great.
But that's all basic stuff. I could go on and on about all the basics and all the things that have changed, but I really want to spend more time on some of the spiciest and most exciting changes because when we first announced Carbon, we promised that we're going to do C++ interop. That was the reason for it all, but it didn't exist yet, right?
It was an idea. So, what I want to do is I actually want to show you C++ interop because C++ interop and carbon is now very much working.
So, let's do this example but now with interop. So instead of importing a carbon IO library, I'm going to import a special package called CPP. Okay. And this package, its only purpose is to let you import C++ code because C++ has a global namespace. Carbon doesn't have a global namespace. Everything in carbon is in a package. We have to put C++ code somewhere. The CPP package.
And what libraries can you import?
Anything. If you can include a header into clang, if you can import a C++ 20 module into clang, you can put it here.
And so this is including the standard library IO stream header.
And now we have the CPP package. And that means we can come down here and print profit and instead of using the core.print that we did previously and if you if you try to use core.print, it's really frustrating. Uh, a year ago, Scott Hansselman got up on the stage and and hassled us for about half an hour.
It maybe maybe a full hour. It grows every time I remember it. But like he hassled us because we didn't even have strings in our print functions. You can only print integers. And he thought that that was terrible and we should do something about it. And when he told me this, I was like, I mean, yes, but we don't want strings. What we want is C++ interop because if we add strings to carbon, they actually have to interoperate with C++ strings. And that's actually complicated. Think about the complexity of the standard library string type, right? We knew we needed to get C++ interrupt before we could even begin to consider strings. So now we can print strings and we do it through C++ interrupt. It's kind of the hardest possible way to do it, but it make sure we actually support this. So let's go into the CPP package and then we can just name something out of C++. Uh, carbon knows that in C++ you use double colons, but in carbon we use dots and so it just translates that automatically.
This is just stood C out that you would normally reference in C++.
Carbon also has a shift left operator and we know that C++ has operator overloads for shift left and so you can also call that operator shift left to print this profit thing right and this works. Now the the problem here is that carbon shift left is not intended for streaming output. I mean C++'s isn't either and it's really painful to use it a bunch of ways but we we made this more obvious in carbon because you can't just keep chaining shift lefts because that has absolutely no meaning for a shift operation. Um and that means that if you if I want to like print another thing I have to add these weird parentheses and like I I this the syntax ends up getting kind of awful here because we're we're faking IO with math. I feel like we can do better.
To do better though, we need to add some something to the C++ that we're using.
That's okay. Carbon has a fun feature for this. So, we we're still going to import the IO string headers, but then we're going to declare some inline C++ code. And you can just write this directly in your carbon file, right? And we're going to look at the what you write in line C++ in carbon without carbon knowing any of this is that this is actually a string literal.
Okay, it's not just a string literal.
It's a block string literal because it allows us to write string literal across multiple lines. It's also a raw block string literal because of that extra hash there. Right? And what this is doing is it's saying like look this is a block string literal and you don't have to escape all of the backslashes which is good because we're about to do stringy stuff where we were going to have our own backslashes and now I can just define a C++ variatic template. This is just C++ code. There's nothing fancy here. We take this exact text and we hand it to clang and we say clang we we we promise as a C++ code could you please do a little compilation for us and it goes and it does that compilation. Carbon doesn't have to know anything about C++ or how it works. We don't even have to know how it tokenizes. We don't care, right? We can just use string literals here, right? And now we can just do something like this is your classic classic C++ version of shift left where we have complex fold operators to stitch together all of the uh variatic arguments that you've passed us and stream them out. And yes, those parentheses are required because fold expressions require parenthesis for reasons I I don't actually understand.
Uh but like you have to and then you shift then you stream out the the new line character. This is kind of what you have to write if you want to write a little helper function like this. So we got a helper function, right? We close up our block string literal and now we can just reach back into CPP and call that function, right? But think about what this is having to do because this isn't just a function. This is a variatic function template. And so what this is doing is it's actually going to clang and being like, I would like to call a function. That function is called print. Um, could you please do all of the C++ machinery that's required? And Clang is very happy to do this. We do overload resolution on that name. We find a function template. So then we do template argument deduction. And like Clang asks carbon, well, what what argument type should I deduce from? We hand it some types, does argument type deduction, it instantiates the template, and then it like like does overload ranking across all of the instantiations it finds, and then it hands back the best overload to carbon. And Carbon's like, "Ah, an excellent function. We will call that function, right? And we'll pass the carbon parameters to it."
Now, the really fun part about this is we have a kind of rule. If you're calling a C++ API, you call it with C++ rules, right? picking that API happens with C++ rules. So we we're not reimplementing something that's an approximation of overload resolution. It is literally clangdoing C++ overload resolution and it's important otherwise things get ambiguous. We'll have bugs.
We'll have divergence. We don't allow any of that. So we have this kind of sharp line. All of selecting and overload happens in C++.
But these arguments are carbon arguments. And so things like implicit conversions, even though the C++ overload rules say here are the implicit conversions that you need to do to select this overload, the implicit conversions have to be carbon implicit conversions. And so if carbon says actually no, an implicit conversion isn't safe there. We actually still get an error message here. This kind of gives us the best of both worlds. We can tighten up rules in some places, but we preserve the exact management of the C++ API.
We can also use it down here for for our run function. Great. Wonderful. We have a lot of stuff. So, this is getting a little bit more fun, but I feel like we can do better here in terms of really highlighting C++ interop. So, what happens if we try and turn this around and access carbon code from C++?
So, let's do that.
First thing we're going to do is we're going to do that in a really spicy way.
We're going to make this vending machine a base class as surface is another fun part about carbon. Carbon has strong defaults uh in some places in case of defining a type. The default is that that type is final. And when you we have this really strong and like like a default we're really happy with you get that default when you say nothing at all. So if you don't say anything, you get a final class. And then when you want to opt out of that, you typically use a keyword to modify to be like, I'm doing something a little special. I I need a base class because I'm going to do inheritance. And Carvin supports inheritance. We support inheritance that tries to match C++ really nicely. So let's make this a base class. And then let's add some more exciting C++ code.
So I have another inline C++ block.
Remember, this is just one file. We're just continuing down that file. I reopen a new inline C++ block. Uh I don't need the hash anymore. I'm not doing strings in here. So I don't have to escape anything. I define a C++ strct called snack as a price field. Right? This is all just normal C++ code. So then I'm going to define a C++ class snack machine. I'm going to do something really wild. I'm going to drive from a carbon class vending machine. So this is defining a C++ class that deres from a carbon class. All right, so there's a base class and it's the it's the carbon class, right? That's really long to type and this is slide code. So I'm going to have a nice little alias called base here. And now in my constructor, right, you have to initialize the base object, the base subobject when you write your constructor for a derived class in C++.
And we can do that in the initializer the member initializer list by writing base and an open print. Well, how are we going to initialize it? Well, we need to call the factory function on that base class. And we can do that. We can even do that through this nice type defaf here. We don't we don't need to do anything. We know that this is a type defaf for the carbon base class. And so we can go find create. And we know that well carbon does dots, C++ does the double colons. And so we translate that for you. And you can then pass five to that. And so now we're calling this carbon function from C++. And we're doing it in an initialization of a base that's a carbon base type but in a C++ derived class. Okay. And we're going to add a uh another method because this is not just any vending machine. This is a snack machine. So we know that we're vending snacks. We're not just dispensing abstract items. And the body of this is going to actually call dispense item. And this is fun because we're calling it the way we would call it in C++. In C++, you implicitly have this arrow. You can find members and you can find members of your base class as well. And this does this finds the dispense item that is a method of the base class and calls it using C++'s syntax because we're in C++.
And now we can go back to Carbon. So we're back at Carbon again now. And we can create our variable machine. It's not a vending machine anymore. This is a CPP stack machine. So we're going to actually use the derived class from C++.
When we create this, this will initialize the C++ derived class will happen by calling it default constructor which will then initialize the base subobject by calling the create factory.
We're going to get a snack out of this, right? By calling vend. No big deal.
Then we can print, right? We can reach and get that field and print it out. And then we can call print profit. Remember because print profit is a method on the base class and we inherited from it. So it's also available on the derived class. It's going to go into everything.
So that's pretty cool, but it'd be more cool if it was live.
So let's see if if this actually works.
And so we can go over to compiler explorer. And I don't know if this is big enough. I can make it bigger for folks. And we can actually take this exact code in compiler explorer. Now there is one difference and I apologize for that. We haven't finished updating the method syntax uh uh the compiler still thinks that you put it in squares instead of in rounds. The only thing it's purely syntactic but we have right we are importing this library and this isn't just like any header. This is in fact the standard library IO stream from lib C++ and we're importing the whole thing. We're defining our print function, finding all the methods. We're calling print down here. We have the second inline block where we derive from this all the derivation works the machine and this actually compiles and run, right? And so this runs with the latest private compiler. All of this is actually implemented and working.
All right, go back to slides though.
Okay, so C++ interrupt is actually in a really great state. Fleshed a lot of it out. A lot of it is getting implemented.
We're we're working really fast to kind of finish implementing more and more interrupt features every day. Uh the I had to keep updating my examples. I was updating my examples this week because a whole bunch of fun things in C++ interrupts started working this week that didn't work last week. Uh the the the functionality is actually landing really really rapidly now. But when we talked about carbon, we had a very particular thing. We really wanted memory safety. And so I want to go back to memory safety and talk about that as well. Memory safety, the importance of it has accelerated in the industry. Uh it's becoming even more urgent to address and and we've actually tried to accelerate our efforts around memory safety and carbon as well. We had expected to have to delay memory safety a lot longer. We've actually started working on a design for it in parallel.
And we think we've got a pretty good idea of how to approach memory safety.
And I'm going to walk you I'm going to give you the teaser version of that here just to kind of wet your appetite. So the goal of Carbon's memory safety design, right?
First off, it better be memory safe or we didn't do what we needed to, right?
Number one goal, we actually have to achieve memory safety just as safe as Rust or anything else, right?
Second thing is we want it to be smooth.
We need enough expressivity in Carbon's memory safety design to represent existing C++ patterns. When you're already writing C++ code and your code already works and it does not have a bug, we want you to be able to represent that in the safe design of Carbon and prove that your code is correct, right?
As often as possible. That's what's going to make it feel smooth because you don't have to change every single way you write code. We can only change it places where it is really necessary to build the proof.
We also need this to be incremental, right? We can't just like throw out the existing code and start again. The larger the chunks of increments, the harder it is to verify any kind of change that you make to code to try and introduce memory safety. So, we really need incrementality and we need two forms of it. We need to be incremental in how much code is migrated to Carbon.
We also need to be incremental in how much safety we're modeling in that code.
So we need to get incrementality in both of those dimensions and they need to work well together.
The design we're taking to try and achieve this involves a strict and a permissive mode in carbon. Okay. Now strict carbon is fully memory safe, right? comporable memory safety at compile time, spatial memory safety at runtime, type initialization, null pointers, data rate safety, just just like you would expect from Rust, right?
This is exactly what we're aiming for.
We want to get to strict carbon. That's the memory safe language people should be using. We also support permissive carbon. And permissive carbon is an intermediate step. It exists in between C++ and strict carbon. Okay, it's strictly safer than C++. When you get to permissive carbon, you're actually still lifting the bar a little bit, but we're going to relax the nonruntime rules so that things don't break at compile time.
If we have runtime safety like balance checking, we can do that everywhere.
That's easy, right? But for compile time safety things, those get relaxed in permissive carbon.
And one of the goals here is that every incremental step you take towards strict carbon reduces the undefined behavior of your code. There's no point at which you start you start moving towards strict carbon and you actually make your code have more undefined behavior. If you add strict checking, it doesn't ever introduce undefined behavior even when unsafe code misbehaves.
Uh and this was a really important principle for us and because this is what allows us to really move things incrementally. But it's also very difficult to achieve.
So let's look at how we can achieve these goals in this design specific with temporal safety. Temporal safety is kind of the core of solving memory safety at compile time. We know how to do it for spatial things. We know how to do balance checking. We know how to do many of the other parts of memory safety, but temporal safety is really tricky.
So let's first break down what we need to solve when we have temporal safety.
So in C++ let's look at what the anatomy of a US free is. There are four parts.
First you have to allocate something otherwise you you don't have a problem here. You have to capture some pointer into that allocation. Okay. You have to do something that frees or reallocates or could free or reallocate and then use that dangling pointer.
Right? These are the four key ingredients that result in use after free bus. Right? In all of the different variations of this, you can find these these four key ingredients.
So what does this look like in carbon?
Let's go through the same set of ingredients. Well, we have an allocation works basically the same way as in C++.
Our st vector is called buff. Uh we initialize it with some stuff. It allocates memory and works just the way it would in C++.
Carbon also supports capturing a pointer into that buffer, right? So you can actually get a a pointer P that points into your buffer. Okay.
And then we can call push back. And this works the same way it would work in C++.
This is going to potentially free or reallocate the the the memory inside of that buff. And then we're going to try to use this pointer. But there's one difference and that's that when we get here, Carbon knows that dangling pointer has been invalidated. And it puts up a compile error message. It says no, you can't use this. is invalidated. Right?
So this is the key we need to achieve.
How do we actually achieve that? So let's look at this now in conjunction with the buff API that allows us to give you that error message. Right? So this is just our type, right? This is your stood vector, but we're going to actually enhance its API to have more information so that we can model this.
The first thing we need to do is we actually need to model where the memory inside the buffer is. We need something in the type system that actually tracks where things are. We call these places or place sets. Um, everything with a little hat is is trying to talk about places or place sets, sets of places where objects might live in memory.
Okay. And this one's not just any set of places. This is specifically an own set of places. And and Buff even does its own allocation. So it's disjoint from all other owned allocations, right? This is this is its own private memory.
Right? And we we declare that up front and we give it a name this hat else now when we go to actually build the API for square brackets right and and the way square brackets work in carbon is an implementation of a generic interface called index ref width.
The return here isn't just going to be a reference to the object, right? It's going to be something more elaborate.
It's actually going to be a reference to an object that lives in a particular set of places else.
Because it lives in that set of places.
We know that the type of amperand x square 0 square has a more precise type than just i32 star. It's an i32 star that points into hat x.
Right? We can model each step of this.
So now we know where P points, right? We have a name for it in the type system hatX.
I'm sorry, we don't have to write this in the actual local decoration. Whenever we're writing locals, we can always omit the places and we'll infer them, right?
We're really good at doing local inference for things like these places.
So that's always done through inference.
So now we're going to call push back, right? When we call push back, there's an extra piece of information attached to that API. It's this invalidate thing. And we call these safety effects.
They're part of the signature of the function. And it says that this function does something that that might change the safety constraints of the program, right? It basically gives us a way of writing into the type system the thing that's in the documentation for push back already. Now, we've made it very explicit. invalidating the memory in hatels else. And every call to push back has the has the potential to invalidate things into hats. Okay.
And so when we call this on the left, we know that that call is going to invalidate P. So we know that P points into the hats that we just invalidated, right? We can tie those together. And that's how we can then see the use of P and be like, no, you can't use P here.
It it was invalidated previously.
Make some sense.
All right. And just keep in mind this these this this hat else this is new information in the API and in the type system that we just don't have in C++.
Uh this is the the fundamental extension you have to make to the language itself in order to model things like uh memory s. And you can see a very analogous thing in Rust which has it has lifetime parameters. They're not quite the same but like it's a very analogous ring. You could also see similar things being added uh in Sean Baxter's safer C++ proposals. Right? This is this is this is the common way of doing it. We've picked a some subtleties around calling these places and sets to try and make them integrate better with C++. But it's all trying to solve the same problem of making APIs more expressive.
But you free is only part of temporal memory safety. We also need to handle thread safe. So let's look at what that looks like.
We're inspired by a specific thing. Clang has a mode called W thread safety or thread safety analysis. So it's been open source clang for over a decade. It's it's actually kind of old. This is based on a commenting discipline that uh Google happened to use uh going back almost two decades for writing mutxes and thread and like multi-threaded code in in a mostly correct way. Uh And we like we took it like well commenting disciplines are nice but like that that's not great. We like to actually check them. Um and so we lifted them up into a set of annotations in C++ and an analysis that uses those annotations to try and and uh enforce some amounts of thread safety and we're modeling carbon's uh thread safety on this. It it gives us a really good baseline. So here here are the core pieces of this. Each mutx right is going to have some set of places that it is protected. Okay, that it is is guarding access to and when you declare a variable you can mark that variable as being guarded by a mutex which basically puts its place into the set governed by that mutex.
Then we can model lock acquisition and release as safety effects. Very similar to invalidate that we just saw, right?
This tracks which locks are held, but it actually has a lot of precision because we can figure out exactly which locks are held through these places and that kind of provenence chain.
And then we can also model lock requirements on API surfaces as essentially requirements on an API surface in the type system. You can't make that call unless the the locked state is where you want it to be. And we can require pointers and references that are shared across threads to also be marked. So we require that you mark them as shared. And the thread sharing APIs all have effects that require those pointers to either be or become shared.
And we can restrict access through shared pointers to cases where you're accessing a variable that is guarded by a lock and that lock is held.
All right. So let's let's put all this together and we're going to put it together actually using this is literally the example on the left from clang's documentation. If you search clang thread safety you'll see this exact code and we're just going to show how this maps into carbon. Okay. So in clang you had this guarded by annotation. you would attach to a field in carbon. We have a guarded uh part of the declaration. We say that this variable balance is guarded by mu a particular mutx.
You have a constraint on an API where locked mu we have to say that this is this requires that mutx is locked.
We have something new. We have to mark these references as shared. If we don't mark this ref self as shared, you won't be able to call this method on an a pointer to the bank account that's been shared across threats. It won't be available because you don't have a shared ref like you have a shared reference and it it requires an exclusive reference an unshared reference.
We also mark shared ref of B because our second function transfer from it doesn't take one bank account, it takes two, right? It takes the one that's receiving stuff and it takes another that's it's uh providing that amount and now we go and we we acquire locks.
You can do this with scoped utility or not but either way this has that safety effect of saying well okay now self do mu is locked.
But here's where things get interesting.
Because the safety effect is built on top of places and is specific to self.mu when we come down and we try and access V, Carbon is going to throw an error. And it's going to throw an error because it's like no like you you locked a mutex in a bank account type but not the one you needed, right? And we actually know that and we can track the provenence of it and we can actually say no, that's an error. Whereas the clang annotations have a hard time with this and it gets worse in C++ because you have templates and other things that obscure this fact from you. But when you have it modeled in the type system, we can actually get very precise and give you guaranteed errors when you when you get it wrong.
Okay, so that's thread safety. Now, there's a lot more to be said about uh memory safety that I I don't have time for.
We've actually proposed a full twopart deep dive talk for the next INDC indic.
So gentle plug to audience members and organizers alike there. But that's where we can get into kind of the full detail of this. There's one other thing I want to dig into here because it's it's especially important and that's how we migrate from C++ into the strict uh strictly safe mode and how that uses the permissive mode and C++ interop to be successful. So let's talk about incremental migration into strict carbon. So there are some things that are explicitly non goals that we that we need to clarify. We don't want to add a large volume of safety annotations to existing C++ code. First off, that would be very difficult. And second off, it would be very ugly because C++ doesn't have good support for these kinds of safety annotations. Its type system can't even model them. In some cases, we don't know how to do this in a really scalable way. And so, we want this to not be part of our goals. We're also not trying to prove arbitrary existing C++ code is safe. Where we can prove that it's safe, that's great, but we don't need to try and prove it for all arbitrary code. That's not the goal here. There's plenty of technically safe C++ code that we're just not going to understand and that's okay.
Our actual goals are to be able to mechanically move from C++ to permissive carbon. Right? This needs to be as trivial and boring of a migration as possible because we need to be trivial and boring to validate. And then you can deploy safety annotations and safety checking in Carbon where we have language features designed to help you.
And then we need to make incremental steps for both of these binding and C++ to carbon migration right and the safety annotations have to be added gradually.
When we say incremental we also mean that the order in layering is flexible.
We can't require top down or bottom up.
We do anything like that we're just going to get stalled. We need that to be flexible as well. It's a tall order.
The two things that let us achieve this is C++ interrupt that we just showed and you saw both directions and like the flexibility of all of that. We also need permissive mode. We need an intermediate step in between C++ and the strict code and that lets us build a multi-step migration strategy where first you migrate your C++ code to permissive carbon. Then you kind of iteratively define what your safety contract actually is in permissive carbon where you have language features to help. And when you do that any strict codes starts being checked and then you can incrementally switch that carbon code from permissive to strict in order to fully check it and find any gaps, any problems, anything that isn't really lined up.
There's an alternative as well. You you don't have to actually migrate the C++ code into carbon to do all of this. you can just wrap your C++ code in Carbon and then do the same series of steps.
And that's important. You're going to have code that you don't own. It's not your C++ code. It doesn't make sense for you to migrate it to Carbon. And so your dependencies are often going to want to do a wrapper approach with, but the code you're maintaining maybe that you're going to want to actually do the migration. Both of these work and they you can you can pick whichever strategy is appropriate for a particular piece of code.
So let's see how this actually works in practice. So, we're going to go through this entire process in increments and show you what the results look like. So, start with some C++ code. This is a a tournament. It's a little bit contrived to fit on the slide, but this is actually code that was extracted from uh the Dawn GPU system. Um, a real real piece of C++ software that folks uh in the browser space have tried to port to Rust for memory safety and found some kind of interesting challenges. All right. So, we're going to port this primitive carbon and it's going to be the most straightforward port in the world. Like the lines just line up, right? We've got venues in both. You've got teams in both. You have stood vector on one side and buff on the other. But this is basically the same thing. This is the most mechanical and boring of like switching the punctuation around as we can make it, right? You've got your method elimination round. Uh you pass it an input parameter of the the matches you want, right? In carbon, you have to explicitly say that that you know this is a a method that can mutate the self object. That's the default in C++ which is always a little bit surprising.
And because you say that you can resize one of your your uh fields, no problem.
We have a venue method. This also takes as a as a parameter, but it does not mutate, right? This just takes uh const uh qualified method in C++ takes a input value in carbon and we return a pointer to one of the locations right we have a whole collection of locations we can return pointers to it pretty straightforward right that's this is just really straightforward let's squish that API up remove all the implementation details and let's look at a caller of that API as well we don't want to just move the API we also want to think about its calls and so we have some function called finals that that is actually using this class. Uh we're going to port that permissive carbon as well. And the ports there are pretty similar, right?
You have a reference parameter in both.
You have uh local variables that work basically the same in both. Um you call methods on objects, you call more methods on objects. Uh one of the only interesting differences here is you you actually have to mark when you're passing a reference to a function. The call site has to have a matching annotation. uh this is an often requested feature in C++ that we we kind of frontloaded into carbon.
Okay, so we have this beautiful permissive carbon. How do we migrate it to strict mode?
Okay, so let's look at that.
So when we go and just switch modes from permissive to strict, the first thing we're going to see is that we're trying to do this resize and this resize function if you remember we had these invalidate effects inside of buff. This resize function is also going to have an invalidate effect. it might reallocate.
And so it's going to say that it validates the that pats_.el uh set of places and the current function doesn't say that it invalidates anything and it's taking self as a reference. And so like its callers aren't exposed to any safety effects that its implementation has. And so the implementation already won't type check here. And this basically forces you to propagate the critical safety information from your implementation into your signature so that we can type check callers for safety, right? And how do we fix this? So let's move that uh carbon code with an error to the left and let's fix it. So the way we fix this is that we add an invalidate effect here. Now we don't want to expose the implementation details of of how we stored the teams. So we just give it a new name hat teams and we go we define hat teams for our type as being an alias for this part of our implementation and this gives us some some nice ability to hide the implementation detail but still propagate the actual safety effect that callers need to be aware of. Okay, that's great. But now let's look at the caller. How does that work? So if we take the caller finals and we move it to strict carbon, we're going to get multiple errors, right? The first thing we're going to do in the in the carbon code is we call elimination round. And now we have an annotation on elimination round. We know that it invalidates hat teams.
Well, we then use L and L is a pointer into we don't know what, but we got it from the tournament. And so it might be in hat teams and it might be invalidated. And so conservatively we'd have to error here and be like we we have no proof that L is valid. You can't you can't use it. We also had the same error we saw previously. There's an effect here that isn't in our signature and so we haven't propagated that information.
Okay. So let's slide that to the left.
Let's figure out how we fix this. Okay.
Well, the first thing we're going to need is we're going to need a distinct set of places from hat teams that we can talk about. So, let's call that hat venues, right? And we can say as well as the venues elements, right? That's that's where all of our venues are going to live.
And now we can refine the API of the venue method to not return any pointer, but to return very specifically a pointer into hat venues. And that's a distinct set of places from hat team.
Right? If you remember the else is always a disjoint and owned set of places. So we know that hat two venues and hat teams cannot have any overlap.
And now we know that we're returning from one of them in this method. Because of that when we get this pointer this pointer has extra type information. It knows that this is actually not just a pointer to a location, but a pointer to a location that's stored in a hat venue's place. That means that when we come down and and colonation round and invalidate some other set of places doesn't bother us, right? We don't have the error, right? We get to use L afterward. It's still fine. We of course also have to propagate and so we have to say also we invalidate the hat teams inside of our team otherwise we aren't propagating the stuff out once. Okay. So you see how we're kind of incrementally adding these annotations, incrementally adding a safety model, and then dialing up the strictness. And we can do that independently. And you can do you can move these things in different orders and and it's it's all it's all going to work out okay. And it's only when you get to both of them that you get the full set of annotations that are required to fully type check this as memory safe.
Okay. So spent a lot of time talking about the technical details. I just want to briefly talk about what comes next.
uh and graduating carbon from the experiment because when we started off carbon we were truly doing sci a science experiment. We had fundamental things that we did not know if we could do if they were even possible right can we design a language that works this way we implement fine grain C++ interop in the way we've been talking about is it possible to layer real memory safety model on top of this um is that model going to be incrementally adoptable from where C++ is can we still achieve the compile time goals we set out to 10x compile time we actually get it can we automate this migration at scale right is it simple enough that we can actually scale it up and can we build an open source project and a governance model and an evolution process that can actually shepherd this going forward.
The really exciting thing is that for these kind of very fundamental and technical questions of does it work, we're basically closing in on yes, uh we're not done. We have some more work to do. We have to we have to dot our eyes and cross our tees, but we can see a very clear path towards answering yes to all of these questions.
and and that's pretty exciting. Okay, so as we get to an answer of yes, that raises the question of what comes next.
We're not going to be doing a science experiment anymore. We're doing something else. I think what we're going to start doing is evaluation, right? And so when we get to carbon 0.1, that's when we're going to try and pivot away from an experiment and towards evaluation. And we need a bunch of things to make that work. We're not ready to do that quite yet. We need to have a working compiler and tools that you know uh users can use for all of these experiments. We need to have that be a modern clang C++ tool chain has to support all of the C++ code that clang does need to support existing build systems. We have to actually implement most of the interop number one thing people need in order to evaluate evaluate this is C++ interop and we have to have a lot of that implemented for this to work. Maybe there's some missing features, but even the missing features have to have workarounds, otherwise you just can't make progress. And we knew we needed to have some amount of design for memory safety given how that's accelerated in the industry. So, we think that this is the right set of things to get to 0.1. We're not even sure. We're actually kind of checking this constantly. We've started doing the earliest versions of evaluations inside of Google already. We're having some conversations with uh technical leads and customer teams to try and show them where we're going, show them what we can do with this and figure out a couple of things. One, are they excited? But two, is this the right set of things for them to continue the evaluation? Or do they need something else? Maybe they need a complete memory safety implementation. I don't know. Uh maybe they don't need that at all and what they really want is more C++ interop. Right? We want to actually start figuring that out because we want to get the smallest set of things we can get away with and still meaningfully enable evaluation.
Fundamentally, we want to release O1 as soon as is conceivably possible. We're trying at every corner to like cut things down and see if there's an even more minimal product that's still technically viable. Uh I hate committing to timelines. I'm not going to try and give you like a hard commitment here, but like maybe early next year, we think we're getting pretty close, but there's still there's still some uncertainty and there's still some work to do. A lot of this is going to depend on kind of the first kind of preview feedback we get.
And if they say like, uh, you need a lot more over here, maybe that changes things beyond that. Just the kind of telegraph we're we're expecting to go to O.2 and that's going to be something different from evaluation. Again, I think that O.2 Two is going to represent moving to pilots. And we only do this if we actually get kind of sufficient positive feedback during evaluation and we've been able to address the blockers that people hit during evaluation. Otherwise, they're not going to be able to do this.
But once we get those two, we want to try and target some limited production usage like real world usage so we can understand how this is working.
So I I hope you all stick with us. We're really excited that we're getting very close to graduation. We really appreciate everything you all do for us.
Uh we're we're super enthusiastic about what Carbon lets you do. Um and hopefully as we get closer and closer, you'll try it out. You'll let us know.
We'd love to have more and more of an evaluation discussion as the pieces start coming together.
So, thank you.
I think we have some time for questions if there are any questions.
Going once. Yes, we have a question.
Absolutely.
So the question is uh have we started having these conversations with folks at Google? What kind of responses are we getting? Uh we have just started. Uh we haven't had a whole lot. The response has been kind of positive although also like well okay it's interesting but we need to follow up to give you more detail. And so we're we're actually doing those follow-ups now and we're we're trying to broaden to like a few more people. But so far there's there's a certain amount of enthusiasm, certain amount of skepticism of whether it will actually work. Uh one of the things that we're definitely hearing from that is that they want to actually try the interop out. They want to try it out in their code with their build system and see it working. Um, and so one of the big focuses for us is actually like implementing interop and getting the compiler and the tool chain ready to integrate into a build system so that they can they can actually use it. So far that's the only like very concrete feedback we've we've heard from folks.
So the question is is the inline CPP kind of similar to the the thing we do with GPUs where there's a separation of hosts and device code. Um there is a similarity but it's also very different.
uh if you if you look at how the the GPU side works um with host and device, you actually have a lot of shared code that's compiled for both and and there's nothing shared here. This is actually just an interop boundary. Um and so so there's there's a difference in that respect. And the other difference is that shared and devices, especially with CUDA, it's one compiler that's kind of compiling both of them, whereas here to a certain extent, these are two different languages with two different grammarss and parsers and compilers, the whole nine yards. What we're doing in carbon is actually just building bridges between two compiler stacks so that the interrupt can kind of cross that boundary.
So, so there's some similarities that you have this embedding, but there's also some differences and it ends up it ends up having some different technologies underneath.
I I don't want to say a faster version of Rust. I I kind of I I'm a big fan of Rust. Uh but we do care a lot about compile time and we we are constantly watching it. Um our compile times are pretty good. Uh there's this fun talk that I'm giving tomorrow about how we're achieving those and I'll I'll actually be showing some benchmarks, but I'm not gonna I'm not going to like give away all of the excitement until then. You should come to the talk if you want to know exactly how much faster the compile time pipes are.
So the question is we plan on supporting OpenMP in the near future. Uh do Yes and no. So if clang supports openmpp and C++ then we'll support interoperating with that C++ we'll support it in the inline C++ blocks that's just clang there's nothing we don't we don't restrict it in any way and so to the extent that there's openmp support in C++ that should work um as long as clang has support for it. If we want to add parallelism extensions to carbon, that's I think a separate question that we should have. Uh and there's a big question of like should those be open MP annotations or should it be a more uh fundamental extension to the language to support uh that use case. I lean a little bit towards a more fundamental extension to the language.
And I'm I'm personally very interested in that. But I also think that kind of needs to come after we we've uh made more progress on having the core of interop and memory safety kind of working and and available. But I do think that we should we should we should be pushing language here. This is another place where C++ is struggling to evolve and you have layers on top like OpenMPP also like CUDA that was mentioned earlier that are kind of layering on top. I'd love to see carbon enable us to, you know, put these things directly into the language and get kind of full language support for the the use cases uh that that those systems address.
>> Um so while at the same time very explicit stuff everywhere as opposed to like something like kind of just put it on top every >> okay let me see if I can summarize this question correctly so so the observation is that uh while we do have fine grain interrupt it is very explicit interrupt you always know that you're touching C++. It's not it's never automatic.
There's a pretty bright line that separates C++ from carbon. Whereas in other programming languages such as TypeScript, I don't know why you want me to sign in.
Anyways, uh other languages such as TypeScript, uh you don't have that bright line. They're actually just a layer on top of JavaScript and and they try to kind of fully integrate all aspects of the other language. And why do we pick one design and not the other?
Uh so there are a couple of reasons behind this. The first reason why we why we made this choice is because we wanted to uh kind of defend against leaking uh technical debt and complexity across that boundary. Uh and we were really worried about that leakage. Um one of the things that is kind of driving that worry is I think I think JavaScript is actually in a healthier place than C++.
is actually evolving and the TypeScript community has a fairly like good relationship with the JavaScript community and and that's working well and so deeply integrating can make sense. You don't need to worry as much about the leakage there. Uh given where C++ is going, we wanted to have kind of a stronger layer of defense against leaking complexity. Uh the other reason though is because of compile time. Uh, one of the biggest concerns we had was around compile time. And the more we kind of don't keep that bright line separation, the less we can really leverage carbon's design and its model to achieve our compile time goals, the more we're kind of hamstrung by having to support any and everything that C++ wants you to be able to do. And so there's a combination of things, I think.
Question.
question is, what is my dream pilot project? Well, I mean, okay, what is my personal dream pilot project to that one is is very clear-cut. I want to uh move parts of the compiler over into carbon because I'm a compiler person. Uh uh when I was first thinking about working on carbon, I actually had another project that I was very tempted by, which is I I have a bunch of interesting ideas of things I want to do to make LVM a much better optimizer. Um, I'm actually I'm actually an optimization person at heart and I spent a lot of time on LFM. And when I was trying to choose what to work on next, the the thing that actually drove me was like, well, I don't really want to do all of this LLVM stuff in C++. I'd really like to do that in a better programming language. Maybe I'll try and get that ready and then I can like go back to like working on the optimizer, but now with a better programming language. So, that's probably my personal dream pilot.
I don't know that that's like the the right pilot for the project as a whole or at a strategic level. That's probably my personal one. Uh I think uh really the the dream like the ideal pilot from a more strategic perspective is uh something that's business critical, right? Uh to some company that's that's actually an essential piece of software we don't want to lose and that doesn't have any realistic path to uh adopting Rust rapidly. and is is being held back by so whatever their whatever their architectural limitations are whatever their legacy is and their brownfieldness is it's it's materially holding them back from adopting uh Rust and they are especially uh sensitive to memory safety bugs if you put those three together I think that's kind of the ideal and and we're talking to at least two projects that that basically have all three of those properties um to see if this makes sense to Any last question once? Well, thank you all so much. Really appreciate it.
Related Videos

TOP 15 Data compression Interview Questions and Answers 2019 Part-2 | Data compression | Wisdom jobs
wisdomjobs
281 views•2019-06-28

CTS 158: 802.11w Management Frame Protection
ClearToSend
4K views•2019-02-04

NDSS 2019 Send Hardest Problems My Way: Probabilistic Path Prioritization for Hybrid Fuzzing
NDSSSymposium
496 views•2019-04-02

How realistic is Cities: Skylines?
CityBeautiful
159K views•2019-02-14

GUIs & TUIs: Choosing a User Interface for Your Python Project | Real Python Podcast
realpython
2K views•2025-04-04

The OSI Model - Explained by Example
hnasr
225K views•2019-05-12

Cloud Computing - Introduction
elithecomputerguy
98K views•2019-10-07

From Traveler's Dilemma to Dynamic Routing | Demystifying Networking
IITBombayJuly
5K views•2019-08-04
Trending

Playstation NO DISC/NO BUY Fight Is Over...
DavidJaffeGames
4K views•2026-07-23

Steam and Xbox Just Dropped The Hammer On PlayStation
OhNoItsAlexx
9K views•2026-07-23

Americans Confused in Australia for 17 Minutes Straight
IWrocker
17K views•2026-07-23

SuperBike Factory Has Gone... What's Next for the Motorcycle Industry?
thatbikersimon
11K views•2026-07-22