HNHacker News
TopNewBestAskShowJobs

nlewycky

229 karma · joined May 1, 2019

submissionscomments
nlewycky··on 6502 back end for LLVM (2022) [video]
> However, this is impossible as per Rice's theorem.

You moved a _little_ too quickly.

There exist program-pairs which can be proven equal, and those for whom no proof exists. You can organize the production of new programs into finite steps and organize the act of creating a proof-of-equivalence between the input and output into finite steps, then execute one step of creating a new program candidate followed by one step of finding the equivalence proof for each of the (finite number of) candidate programs created so far. In this way you are guaranteed to find an output program and its equivalence proof whenever such an (input-program, output-program, equivalence-proof) tuple exists.

Finding the equivalence proof is recursive enumeration—the same as creating the program candidates—but in some machine verifiable proofing language.

Speeding this up by leaving out syntactically incorrect programs and equivalent programs, as well as defining the proofing language and implementing the checker are left as an exercise to the reader.

nlewycky··on What I learned getting acquired by Google
Temps, Vendors and Contractors.
nlewycky··on C and C++ prioritize performance over correctness
You started the discussion by saying that we can understand what the committee meant by simply reading the text in the standard that they wrote. Remember? Why then does it matter if it's non-normative text (aka. informative)? If it's non-normative it's there to explain what they were thinking.

I double-checked my copy of the C89 standard (draft), and as far as I can tell the text is normative. Non-normative text includes footnotes and appendices and editor's notes in square brackets, but I didn't see any of those involved. Sometimes there's a section that states 'the following text is non-normative' but I didn't see any of that either. Why do you think it's non-normative?

If that text is non-normative, where is the normative definition of undefined behavior?

Regardless, I think I understand what's happened here. If I may? You've read the text and you've repeatedly commented that we have to discard the "it allows you to do anything" interpretation because that interpretation is nonsense.

I'll stipulate that when reading English, we regularly have to discard nonsense interpretations. "Out the window, the mountains looked over a beautiful lake", we discard the interpretation where the mountains have eyes and are looking out a window. This is a normal part of reading English.

I posit that you believe that the "anything can happen" interpretation is impossible so strongly that no possible wording would ever lead you to the interpretation intended by the committee.

So instead, how about I explain why the committee chose to define UB this way? How it isn't nonsense?

It's so that a C compiler could use the plain "add" instruction for an addition in C across all the crazy CPU designs. Here, let me make a simple example, this isn't a real CPU. Suppose the CPU has a status register which contains "signed overflow" as one of its bits. This bit is set or cleared when you do an ALU operation, including ADD. The same status register is reused when doing a memory operation, but that bit is reused to indicate whether you're going to the first or second bank of memory. The CPU authors think that this is great, if you're doing pointer math and you add a pointer and an integer then you transparently overflow from the first bank of memory into the second and it looks like a contiguous address space! The system integrator (or, motherboard designer, roughly) decides to use bank selection for a different purpose. There's no way the first bank of memory would ever be completely full (nobody buys or sells that much RAM) so they put the RAM on bank 0 and the I/O ports on bank 1. Their system has memory-mapped I/O! So far, everybody's done something that seems sensible to them. Third, the C programmer writes "*p = x + y;". What happens if x + y are signed ints and the addition overflows? The signed overflow bit gets set, then the STORE instruction accesses the I/O ports instead of the memory!

Is the C compiler buggy for not inserting an extra instruction that clears the sign bit of the register? The committee intentionally decided that no, this is how signed integers should work and if you want potentially slower integers with guaranteed semantics that you should use unsigned integers instead. I think that attaching well-definedness to signed/unsigned was a bad move, but this is what they did. (And it does what you've said you want in other comments: the + in C becomes whatever the machine ADD instruction does!)

The C committee invented undefined behavior as a way to ensure that the compiler really could ignore the situation. (FWIW, real CPU ISAs back then had all kinds of interesting ideas. We hadn't yet agreed that bytes are 8 bits. Or that we should use 2's complement. Some designers looked at division by zero as an invalid operations and thought that this was an excellent feature that should be brought over to other operations like add and mul, hence "trap values" in the C standard.)

C and Unix were a commercial success (CPU firms could skip writing their own OS every time), and starting then, CPU designers made ISAs where C could be easily lowered to efficient assembly on their machines. This notion that there was an efficient lowering from C to the CPU, at the time C was standardised, is an anachronism. C created UB to handle then-contemporary CPUs, then later CPUs created ISAs that matched C syntax. If you don't work in compilers or assembly or CPU design, this might be surprising, but if you aren't intentional about making your ISA work well in C, it's easy to accidentally make one which doesn't. Intel MMX famously couldn't be targeted from compilers because the compilers don't have sufficient information to solve where to put the necessary EMMS instructions. Oops!

Decades down the road, CPU designs evolved and started creating new patterns that don't match C well -- and the C language didn't evolve with them. What expression is PSHUFB in C? Or VPMASKMOVD? The CPU firms knew enough to make sure that the compilers could support their new instructions, so they added these as CPU-specific extensions. If you wanted them, you had to write non-portable code that only worked when compiling to target their CPU and not others.

The compiler engineers believe in C being a portable language. If you write code using SSE or AVX and compile it to ARM with clang, it will compile and port the SSE builtins to ARM Neon vector extensions. Doing this required the compilers to be a whole lot smarter about how the code works, and is a large part of the source of modern complaints about compilers "exploiting" undefined behaviour. I counted 23 ADD instructions in contemporary x86-64, assuming you include fused instructions (LEA) and exclude things like OR (saturating add without carry between bits) and XOR (addition with 1-bit vector lanes, lol).

Finally, C defines an abstract machine. In this context, machine is a "term of art" in computer science, popular types of machines include cellular automata (the machine side of the 'regular expression' language), push-down automata and Turing machines. If you've seen those before, you may know that they're usually pictured as a directed graph, with states drawn as nodes and state transitions as directed edges, the edges labelled with the circumstance under which this edge is taken. Now, C's machine and the three I listed have a key difference, those three are all decision machines, meaning they exist to either accept or reject an input string. The C machine is a functional machine, it describes a function that transforms and input to an output. (In this treatment, you may picture side-effects as being part of the output.) The C standard defines how such C abstract machines are written down (the C syntax) and what semantics they have: states, and state transitions. The question is what happens when you are in a state and receive an input for which the standard does not define any particular state transition? In a cellular automata or PDA or Turing machine you have a single state named "error" (or "reject") at which point you reject the input string as not being a member of the set that the machine is deciding (aka., your input string fails to match the regex, and we're done). In a functional machine, we don't traditionally have such a state. By defining UB in the way they did, the C standard is stating that when no state transition is specified, it may go anywhere, including to states that aren't required to exist and on which the standard places no requirements.

nlewycky··on Using uninitialized memory for fun and profit (2008)
That's impossible in the general case, but there are compiler warnings that try to help you catch as much as they can, use `-Wall` when building code, and there is runtime instrumentation `-fsanitize=undefined` to catch as much UB as possible without breaking the ABI. Also consider using `-fsanitize=address,undefined` while developing and `-fsanitize=thread` for threaded code. `-fsanitize=memory` is a bit difficult to deploy because you need all your libraries (except libc) to be built with it too, including your C++ standard library if you're building C++.
nlewycky··on Using uninitialized memory for fun and profit (2008)
> 1. it's insane. nobody would write such an optimization because there's no possible performance gain, and it changes program behavior

LLVM explicitly has an 'undef' constant value which facilitates this optimization. https://llvm.org/docs/LangRef.html#undefined-values

FWIW, the most important reason compilers do this is to decrease compile time. The compiler notices that some code has undefined behaviour and deletes the code now instead of waiting to prove that the path to this code is unreachable. The later optimizations tend to be slower and scale badly with more code in the function, so deleting it earlier will speed up the compilation.

> 2. malloc is just a function, it's not treated in any special way

The compiler is full of optimizations that treat malloc and other functions specially. This file implements an analysis, but the results of the analysis is used by transformations. https://github.com/llvm/llvm-project/blob/main/llvm/lib/Anal...

> 3. there's no guarentee this malloc is a per-spec malloc, it can be a user-defined function for which this is perfectly valid

Yep, there's a flag for that mode, `-ffreestanding` which corresponds to freestanding mode in the C89 standard, section 2.1.2.1. Without that flag, we assume malloc, strlen, etc., are the standard library functions that do as described in the standard.

nlewycky··on California wants to build more solar farms but needs more power lines
> When they're all done they'll be providing power cheaper than your home solar setup, even with their additional requirements

Really? Could you cite a source with some numbers? On my PG&E bill, the cost of power generation is about 44%, the remaining extra requirements (transmission/distribution, billing, legal compliance, etc.) are the other 56%. Based on that I extrapolated to thinking that the long term future of electricity is to remove the grid because the cost of moving power around exceeds the cost of generating it, but that extrapolation makes for a big assumption. I'd love to see an analysis if you know of one?

nlewycky··on C and C++ prioritize performance over correctness
If it doesn't change whether the argument is correct or not, why did mpweiher bring it up?
nlewycky··on C and C++ prioritize performance over correctness
> 1. It gives a range of 3 permissible options. If one of these "permissible" options is "you can do anything", what are the other 2 options doing on that list?

Before the list it clearly says, "behavior, [... when UB occurs elided ...], for which the Standard imposes no requirements."

How can it both impose no requirements, yet simultaneously impose a requirement that it come out of that list of three options?

> That this somehow (how?) means "I can do anything" is purely your interpretation.

Purely mine?

* https://devblogs.microsoft.com/oldnewthing/20140627-00/?p=63...

* https://blog.regehr.org/archives/213

* https://blog.llvm.org/2011/05/what-every-c-programmer-should...

* https://stackoverflow.com/questions/32132574/does-undefined-...

* https://en.wikipedia.org/wiki/Undefined_behavior#Examples_in...

* https://www.youtube.com/watch?v=ehyHyAIa5so

I must have an amazing number of sock puppet accounts.

nlewycky··on C and C++ prioritize performance over correctness
> Very clearly gives a list of "permissible behaviors"

A statement like "we have products ranging from cake decorating to peanut crocheting to jousting lances" does not mean that this is a list of the only three types of items in the store. This construction is called a "false range" in English. When you have a range of something that does not have an order, it means "varied things, left unspecified". It's very clearly not supposed to be an exhaustive list, merely a few examples.

So the standard lists three examples. The first is that the runtime behaviour of the program may do any-unpredictable-thing. The second is that the compiler may 'behave in a documented manner' and maybe issue an error. They wanted these two examples because they didn't want any misunderstanding that UB was limited to what could be shown UB statically at compile time, nor that it was limited to only having effects on the program at runtime.

I'm not honestly sure why they bothered adding the third "oh, the compiler or the program terminates with an error message". I could speculate that this is what they wish would actually happen, and including it in the list improves the chance of that.

> unpredictable behavior mentioned in the spec is that of the environment, not the compiler.

I don't believe that's correct -- I think it appertains to the program not the environment or the compiler -- but it doesn't matter either way. The environment is responsible for supporting execution of the program, so if it's unpredictable then it follows that any unpredictable things can happen to your program -- it would be like trying to run on a CPU that's experiencing physical failures.

nlewycky··on C and C++ prioritize performance over correctness
> And the existence of pre-standard C compilers that worked is, of course, clear evidence that it is not a necessary condition either. Or at least was not.

The difference is that those compilers didn't have a spec to follow or disobey anyways. The can always just say "whatever happened is right" and you could argue that the compiler did something _unhelpful_ but the compiler broke no promise because it made no promise.

> Particularly because the creators of the standards clearly stated so. In the very standard itself.

They clearly stated the opposite:

""" * Undefined behavior --- behavior, upon use of a nonportable or erroneous program construct, of erroneous data, or of indeterminately-valued objects, for which the Standard imposes no requirements. Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message) """

from ANSI C89 (§1.6).

The compiler may ignore the situation completely leading to unpredictable results. If the compiler ignores a situation, such as a signed integer overflow, and that leads to unpredictable results, such as "next time we see an if-statement, execute both the if-block and the else-block", then that's 100% conforming. That's exactly what was intended. This sort of thing can happen, suppose the evaluation of an if-condition ends up in some CPU flags, and the compiler knows the two flags are exclusive because the only way they wouldn't be is if the program had a signed integer overflow.

FWIW, GCC 1.17, released in January 1988, would launch 'nethack' during the compile if it detected a #pragma that it didn't understand. The idea of interpreting it as "anything can happen" is neither incorrect nor new. (Technically in this case, an unknown pragma has implementation-defined behavior which is the same as UB plus a requirement that the behavior must be documented in the compiler's manual.) It was a bad idea though and they removed it because a compiler that does that is, well, not good. We call this property "quality of implementation", but it wasn't a correctness issue.

nlewycky··on C and C++ prioritize performance over correctness
> The standard joke is a joke, because it is wrong.

No, it is a joke because it is silly.

It is correct and intended for pedagogy.

nlewycky··on C and C++ prioritize performance over correctness
> > until the spec tells us what meaning it has.

> That's a commonly held belief, but wrong. My first C compiler predated the ANSI C standard. Yet the code very much had meaning, just not in terms of the C standard.

Much like /* plain English comments */ have meaning, just not in terms of the C standard.

Talking about prestandard C isn't relevant because we're talking about what a standards conforming compiler can do with undefined behaviour per the text of the standard. I do not find your counterexample compelling.

> The C standard defines the minimum a C compiler has to do to be in compliance with the C standard.

The standard does not define what a compiler has to do. Instead, it defines the C abstract machine as well as syntax attached to behaviour in terms of that abstract machine. When a program which is restricted to that syntax is executed, it must have the effects (upon the C abstract machine*) as specified in the standard.

Sometimes the standard doesn't specify anything for a given syntax in a given abstract machine state, and sometimes the standard explicitly specifies that "the behavior is undefined".

> It is not a complete definition, and this is intentional.

I'm not sure what you mean. Compiler extensions exist (and are generally regarded as a necessary evil), is that what you're referring to? Or are you referring to implementation-defined things (all of which are explicitly called out in the standard)? Or something else? A citation, if you can?

* Notably the C language says nothing about the real machine. Supposing you have an I/O port at address 0x9000, accessing "*((char*)0x9000) = 1;" does not need to access memory address 0x9000 at all. Of course, compiler engineers are not silly and implemented it as accessing 0x9000 on the underlying machine. Similarly the standard committee is careful to ensure that the language it defines can be implemented without needed the overhead of a VM or interpreter, though sometimes they get that wrong. (For example, see the issues combining multiple alloca() and C99's "int foo[*];" in the same function.)*

nlewycky··on C and C++ prioritize performance over correctness
No maliciousness is required. "1 + 2" produces "3" only because the standard defines it so -- it is therefore required that 1 + 2 produce 3. When the standard does not say what to do, there is no requirement.

You said "it's not a license to do whatever" which is only true because it isn't a license. The code can still do whatever, because there is no requirement that it do anything other than "whatever".

You seem to be arguing from a position that the code has meaning unless somehow UB gets involved, but the reality is that these bits and bytes don't mean anything until the spec tells us what meaning it has.

nlewycky··on Alphabet Announces Second Quarter 2023 Results [pdf]
You build brand new data centres with the latest technology. When a new technology rolls out, new builds always get it, and you start upgrading your newest data centre first. Sometimes you never make it to your oldest data centre.

Data centres fill up. They fill up with important "production" software and associated data housed in the same DC for locality, which never moves and mustn't be shut down. After a while the data centre is full and everyone needs to put their new software in the newly built empty data centre.

Over time a data centre gets technology upgrades later or not at all (say, a new-new networking fabric is invented and starts rolling out before the new networking fabric finishes rolling out). There's a slow decay in terms of service. New data centres have on-site offices of engineers who keep everything running at maximum efficiency, older data centres have a single security guard who receives a daily printout of which machines to press the reset button on while doing their rounds. Eventually an old data centre is turned off all at once.

If they're keeping older servers around longer, they may be delaying turning off the least valuable computers, continuing planning upgrades to 'new' even after 'new-new' is ready and rolling out, or slowing down the invention of new tech. As I understand it, "decommission X many machines due to age" isn't really the model.

nlewycky··on Valgrind recieves a development microgrant from FUTO
ASan will have false negatives on the memory accesses performed in the code which isn't compiled with ASan. It works fine, otherwise. FWIW, UBSan (-fsanitize=undefined) follows the same rule. For this reason it's common to combine them and build once with -fsanitize=address,undefined. Don't forget to pass that while linking too.

ThreadSanitizer requires that all code with atomic accesses be instrumented.

MemorySanitizer requires that all code which writes to a byte in memory be instrumented.

nlewycky··on Valgrind recieves a development microgrant from FUTO
> Valgrind is like all the sanitizers (memory, thread, leak, address, undefined) all together, plus performance and memory profilers, all for uninstrumented code.

Valgrind as memcheck can do memory and leak and partially do address sanitizer, and when run as helgrind can do thread sanitizer. Valgrind doesn't do -fsanitize=undefined.

Undefined -- UBSan -- in particular doesn't overlap with valgrind. Your CPU uses the same instructions for signed and unsigned integer addition (thanks to 2's complement) but UBSan will catch signed integer overflow. UBSan will catch misaligned pointers whether the CPU permits unaligned accesses or not. UBSan will catch integer and float divide by zero, which valgrind ignores because at the CPU level those have defined behaviours (of setting an error flag or whatnot).

As for partially doing address sanitizer, ASan improves its probability of catching invalid pointer arithmetic by intentionally spacing out stack allocations and global variables in a way that the compiler is allowed to do but which valgrind can't because valgrind has to emulate the CPU instructions as they lie. ASan also replaces malloc with its own version that attempts to give different addresses as much as possible so that the use of a freed pointer is maximally likely to not accidentally point to memory which has since been reallocated.

Optionally ASan also includes special features like "after running the inliner, malloc a fresh stack for every function call" so that you can really catch stack use after return https://github.com/google/sanitizers/wiki/AddressSanitizerUs...

I'm not deeply familiar with either ThreadSanitizer or Helgrind. The TSan devs claim that Helgrind either runs in "catches too few bugs" mode or "has too many false positives" mode and that they got it just right in TSan. I can't evaluate that claim. https://static.googleusercontent.com/media/research.google.c...

nlewycky··on Toyota has been developing a solid-state battery for EVs with a range of 745mi
The grid is going to explode. Let's start with California. Because they're a large economy, a large population centre and a famously leading on environmental issues, they tend to run into scaling and environmental problems first.

Do you remember news about the California grid straining under the heat wave in 2022? The governor sent text messages to every Californian asking them to minimise their power usage? Power consumption across the state* reached 52GW.

Every April, by the rules of the Federal Energy Regulatory Commission, each state receives submissions of new projects that people want to build and connect to the grid. Each of these is called a "Cluster". In April 2021, cluster 14 included a proposed 110GW of new power generation. This was so many submissions that the state couldn't even finish their legally mandated analysis of all of the proposed projects in time for the new submissions for April 2022, so they pushed Cluster 15 back to 2023 (approved by FERC). It's past April 2023 and Cluster 15 projects proposed 354GW worth of power. If we take that CA can produce 50GW now, and add clusters 14 and 15, that's a little over 10× our current maximum power generation. You could argue that maybe some of these projects won't get built, that always happens in every cluster, but the number of withdrawn applications is a smaller percentage than usual.

Estimates are that EVs will require us to double our current power generation.

The glut in new power construction is not a California-specific phenomenon. https://www.ferc.gov/news-events/news/ferc-proposes-intercon...

* technically across the California Independent State Operator, https://caiso.com , which is about 80% of California and also includes a tiny bit of Nevada for geographical reasons.

nlewycky··on Google and HTTP (2018)
> Do LAN sites need HTTPS? IE wifi router, printer, fax control panel?

Of course. All traffic needs to be encrypted. Why must these not be encrypted?

> I am pro-HTTP for these use cases for as long as browsers have more serious warnings against self signed certs, old SSL/TLS versions and weak algo choices than the warnings for HTTP.

IMO, HTTP-over-TCP support should have been removed from all browsers years ago, solving your self-cert vs HTTP problem.

Good solutions for the LAN use case have been rather weak so far. The two categories of solutions are a) a CA for your LAN. People are OK using this for things like dev servers on localhost, but what you want is to have router that issues DHCP leases also issue IP certs for local devices. b) TLS-TOFU for self-certs. We can even do this over the public web too, but we can also treat self-signed certs on LAN vs. public web differently and report when the identity behind the IP has changed. (Generally a bad idea over the public web because we want to allow key rotation, but that might be fixable by adding multiple keys or some hack like publishing the next key into .well-known/next-key or something -- I'm riffing here.)

> Hardware deserves to be supported as long as it physically works rather than as long as its embedded TLS stays supported

Why? Or put differently, if the manufacturer has stopped providing firmware updates, they've unsupported it no matter what the rest of the world has done. Why should the rest of the world compromise security by design in order to only partially not-break a hardware device that the manufacturer doesn't support? In this one exact scenario? Maybe in the future we can provide a small ethernet-to-ethernet (or wifi-to-wifi) device that wraps a single old TLS device and provides a modern interface to it, if support is that important.

nlewycky··on Google and HTTP (2018)
Could you elaborate? I thought upgrading to HTTPS wouldn't involve redoing any sites, no changes to the HTML/CSS/JS/server side code, except for the TLS configuration and maybe replacing links from "http:" to "https:" but that's optional. I'd like to hear how it requires redoing a website?
nlewycky··on Google and HTTP (2018)
I can't READ THE DOCUMENT because it isn't on the web. Do you have an https link? Or a mirror somewhere on the web?
nlewycky··on Google and HTTP (2018)
Nonsense, all sites need HTTPS. Any HTTP connection can have its contents rewritten by an middle party, so if you are using HTTP and not HTTPS then you are 100% OK with the recipient getting altered or incorrect data. If that's the case, just delete your content and serve a 404 instead since you've already stated you're OK with this.

Why do you think "handling user data" is the only reason to employ encryption? What if the website is serving up static content that seems completely fine to you or I but the user is in a legal situation where they would be in trouble for reading it? What if the user is somebody the local police are trying to frame, so the request was normal, the response was normal, but the response was replaced with something illegal so the police can arrest for possession? Why should every router on the path be able to see what I'm reading, what I'm reading web pages about medical conditions? Why do you insist that everyone should collectively make technical decisions that serve to enable this kind of thing?

This isn't hypothetical. A country once used their position of owning the local telecom to inject javascript into web pages that would DDoS another website they didn't like. See https://en.wikipedia.org/wiki/Great_Cannon .

The web moved from HTTP-over-TCP to HTTP-over-TLS because it fixes real problems. One of the common mistakes people make when thinking about security is trying to imagine the attacks themselves and then only defending against those. Your imagination is not the limit, you're up against the collective creativity of everyone else. Encrypt your stuff and remove a whole category of possible problems.

nlewycky··on Ohio cryptic lineage update
If you mean analysing sewage for COVID-19, then yes. I've been monitoring COVID levels in my community via https://covid19.sccgov.org/dashboard-wastewater .
nlewycky··on Launch HN: Type (YC W23) – AI-powered document editor
Do you have a privacy policy?
nlewycky··on Dropping support for old C++ standards
As far as I know, there was only ever one version of LLVM at a time in the monorepo. It's possible things changed after I left (either the compiler team or Google).

Each upstream commit didn't land directly into the monorepo, instead there was a long lived branch, and on the compiler team there was a buildcop rotation responsible for doing an integrate from that branch into //third_party/llvm. This included running the tests (and fixing any problems) for any other software that depends on LLVM as well as building an unstable crosstool and doing some basic smoke tests on that. Taking that crosstool through testing and to stable crosstool was the responsibility of a different buildcop rotation, using a special compiler team tool for testing the testing crosstool nightly, then to release to stable we used the ordinary presubmits, but for all projects at once, making its testing as similar as possible to any normal code change.

nlewycky··on Dropping support for old C++ standards
It does. Google mirrors every commit to LLVM in their monorepo, builds and tests the whole monorepo with a fresh Clang nightly, and (ideally) one of those Clang nightlies is released as the new stable compiler for all users of the monorepo every week. This helps keep Google at HEAD and helps keeps LLVM upstream stable.
nlewycky··on Non-ECC memory corrupted my hard drive image [video]
If this is the same issue at the same well known tech company that I recall, it wasn't a hardware failure but a case of undefined behaviour. The existing code used memset to initialize a struct with zeroes, then someone else added a virtual method (to it? to a base? or member?) which caused the struct to grow a vptr. Now the memset was clobbering the vptr. They compiled this program with some form of link time optimization and the compiler emitted assembly that tested the vptr against one of two known values, and when it didn't match (because it was NULL) it must be the other known value! Either way it didn't use the vtable, it just made direct calls to the appropriate member functions, which led to a "drop table". As part of resolution it also led to https://reviews.llvm.org/rL130299 for compile-time checking and also UBSan's `-fsanitize=vptr` for runtime checking.
nlewycky··on Microsoft bakes a VPN into Edge and turns it on
> No, I am not going to do work on it, any work, at all.

Without HTTPS, the content can be replaced entirely. Last time it was JavaScript that DDOS'd github. If you don't want to serve content over HTTPS, then you don't care what your users receive. Just delete the site and they all get 404's instead, since you already admit that you don't care either way.

If it makes you feel any better, HTTP without HTTPS was a mistake we all made together. It should never have happened.

nlewycky··on Show HN: Modifying Clang for a Safer, More Explicit C++
I'd like to be able to redeclare a member variable from the base in my derived with a covariant type.

  class FontInfo { /* ... */ };
  class TTFFontInfo : public FontInfo { /* ... */ };
  
  class Font { protected: FontInfo *info; /* ... */ };
  class TTFFont : public Font { public: using TTFFontInfo *info = Font::info; /* ... */ };
making TTFFont::info an alias for Font::info but with the derived type.
nlewycky··on Show HN: Modifying Clang for a Safer, More Explicit C++
break and continue with an integer argument, how many times to break. "break" breaks out of nearest loop, "break 2" breaks out of two. "continue 2" breaks out of one loop and then performs "continue" on the loop outside that.
nlewycky··on Show HN: Modifying Clang for a Safer, More Explicit C++
IMO it would help adoption if you supply a clang-powered rewriter into and out of your language variant. It allays the fear of losing your codebase if the compiler project dies.

Reverse the default for typename. Currently some_class<T>::thing is assumed to be an expression where 'thing' is a variable, when we don't know which template pattern to use because there may be an explicit specialization on the T that the user chooses. Hence, we have to say "typename std::vector<T>::iterator it;" instead of just saying "std::vector<T>::iterator it;". Instead, reverse that and assume it's a type by default unless shown that it's an expression. You'll need a new keyword for that, replacing "typename".

Remove the promotion-to-int rules. Currently in C (and in C++)

  unsigned short test(unsigned short a, unsigned short b, unsigned short c) {
    unsigned short x = a * b * c;
    return x;
  }
can have UB as signed integer overflow because any math done on an object smaller than int gets promoted to int. (No, you can't fix this with "(((unsigned short)x) * ((unsigned short)y))" the promotion happens on 's LHS and RHS, if those have types smaller than int.) Beyond this, people seem to expect that the type of the variable declaration will appertains to the calculation on the right, but it doesn't. For instance people seem to think "float f = a + b;" can't overflow where 'a' and 'b' are ints, because the assignment is going into a float.

I haven't thought this idea through completely yet. Extend pointer types to include a static allocation identity as part of the type. Address-of local variable or global variable should produce one of these pointers. A "static allocation identity" is a special-typed zero-size variable, so you can stick it in code or as a class member. You could have pointers that were guaranteed to be allocated by THIS allocation point, instead of pointing to every possible T in the program. I'll fake up a syntax, "tree_node ^ tree::node_alloc ". It's known not to alias any other TreeNode the program might have, it has to be attached to the allocation point owned by that specific "node_alloc" in that object. (Let me phrase it differently. A tree in C or C++ has pointers which can point anywhere as long as it's another tree node type. That could be pointing to a different tree, it could be a self-pointer, it could be pointing up the tree, and so on. If your tree_node class has an allocation root, you can say that the pointers are things allocated through this allocation root. They can not outlive the allocation root. They are distinct from the things allocated by other allocation roots, which are the same tree_node types, but different tree_node objects. The node's list of children is std::vector<std::unique_ptr<tree_node ^ node_alloc >> so it clearly only holds pointers it allocated itself.)

There's another problem with pointer related to the above. Some code I saw used a "T &get_or_default<K, V>(Container &c, K key, V &default);" and the problem was that people would call it with a temporary for the default, like "Value &x = get_or_default(mymap, key, Value());" and they'd be holding a dangling reference. If you could make that an error, that'd be great. Maybe we use a trick like the "allocation root" above and treat pointers or references to temporaries have different type from the local variable. Then get_or_default takes and returns a reference-to-temporary and attempting to assign that to a reference in a variable declaration fails. Unlike the previous "allocation root" idea where you indicate the only thing you accept, this would be a case where you accept all allocation roots except one, the "temporaries" allocation root.

As far as I know, no compiler takes advantage of the freedom of the order of operations except in the most trivial ways. Everyone knows that in "f() g() + h()" that * must happen before +, but people think this means that f() and g() must happen before h(). No, they may happen in any order at all. I had to fix a lot of code that did "Print(stream.read(), stream.size())" where "read" updates the pointer and leaves size == 0: gcc ran stream.size() first and clang ran stream.read() first, setting the subsequent size to zero. Similar issue with "expr1() = expr2();" expressions.

Extend switch() and case to work on objects with any operator== defined. Add a statement for fallthrough and default to 'break;' before the start of the next case-label. Give each case label its own scope so I can declare variables in there without adding my own curly-braces. (Bonus 1 can you design a way to ensure that case labels are not overlapping? May require something other than operator==. Bonus 2 can you allow cases to be structured binding matches, similar to Rust?)

Speaking of structured binding, it's great but doesn't allow nesting. This

  std::vector<std::unordered_map<std::string, std::pair<int, int>>> v;
  for (auto [name, [lhsid, rhsid]] : v) {
is code I actually wanted to write in the past week yet that's a syntax error.

Add the ability to declare object inheritance ("class Derived : Base;") so that I can cast between them before writing out the body of the derived class. Also allow me to write out the entire class tree with no possibility for extension in another translation unit. The "final" keyword states that a class may not be derived from, but I usually have a Base class which does have subclasses, but a known list of subclasses that will never grow without recompiling the whole project. Currently the compiler has to assume I could write a new subclass and compile it into a shared object that the existing program dlopen's and the existing program will work. It's crazy. No, I have the final tree not just some leaf classes, please devirtualize the whole thing for me.

Are ABI changes on the table? Explicit template instantiations and explicit specializations should mangle differently. See my comment elsewhere: https://github.com/dealii/dealii/issues/3705#issuecomment-11...

If I think of some more, I'll reply to myself.

← PreviousPage 2 of 3Next →