HN user

int0x80

488 karma
Posts2
Comments260
View on HN

There is one common pattern which is required in many use cases when working and representing ASTs - having a `parent` node, or having access to parents and children. This complicates ownership implementation.

Additonally, is essential to provide an API to transform trees or even construct new ones using immutable ASTs like implemented in many compilers eg .NET Roslin or typescript TSC.

In this formulation, a language server needs to just enough analysis to drill down to a specific node.

Does rust-analyzer compute semantic information on the fly and incrementally?

Sometimes, you have to make a complex feature or fix. You can first make a prototype of the code or proof of concept that barely works. Then you can see the gap that remains to make the change production ready and the implications of your change. That involves fixing regressions in the test suite caused by your changes.

Exceptions are a very useful tool. In your example, the main program logic is buried in error handling which makes it more difficult to read the code and the code becomes more complex, leading to more bugs. In many cases, it's preferable to have a single error handler centralized in one place, out of line of the main logic. This makes the code more readable, reduces complexity and duplication.

I know very well what it means to care about a memory allocation cost or an atomic operation in a fast path. In some applications though, that is not the dominating performance factor, instead the algorithm dominates. For example, with C++ an manual memory management our application could take something like 58 minutes to run while in Java it would take 60 minutes. The impact of the low level management is entirely insignificant for performance in the big picture in these cases.

The performance comes from the big O complexity. Memory management is a constant factor in the algorithm.

When I mention constructors I am talking about initializer lists and all the subtleties and special cases around them.

Basically most of the complexity I mention comes from memory management. It's just a lot of details to know about and keep in your head. It's the price to pay for the flexibility and power.

In Java you need to understand references and object lifecycles or you get memory leaks and slow applications.

Actually, I work on a performance sensitive application and it's written in Java and it's a good choice for it, since the performance comes from algorithmic complexity, mostly from asymptotic complexity. A C++ rewrite wouldn't significantly impact the performance of the application.

I find today's C++ extremely challenging to pick up speed for someone coming from let's say, Java. Smart pointers, pointer/references, rvalue reference, copy/move semantics, (perfect) forwarding, constructors, and how all that interacts with templates. It's just so unwieldy complex. It takes me hours to write something that would take less than a minute in Java even though I have experience with plain C. The worst thing is that after it finally compiles I feel I can't be sure if I've followed all the rules and best practices correctly or if it's going to blow up spectacularly and potentially unsafely at runtime. The fact that a book like "Effective Modern C++" is needed with all those traps and foot guns to be aware of is ludicrous.

And don't get me wrong - I think C++ is the most powerful language out there on the right hands - it just feels to require a lifetime of learning to became productive on it.

If a static analyzer is sound, which is something that can be mathematically proven (formal method), will find ALL existing issues plus some false positives if it's not complete (which is almost always the case).

Fuel taxes don't address the same issue. Availability of chargers is a blocker for EV adoption. The measure directly helps with that problem, while fuel taxes is a more indirect push. In other words, even if you are heavily pushed by fuel taxes, if you have a blocking barrier with charging infrastructure, buying an EV won't be feasible to begin with.

Because most problems like merging and resolving correctly, or rebasing or squashing if needed don't get resolved by cloning a new repo. If you avoid learning the tool and you just clone a new repo when you have a problem you won't be able to do any non-trivial task in git.

I find this as being indirect and dancing around the issue. When I do e.g. a code review and I see a glaring defect on it - I point it out very clearly and directly to make sure there is no misunderstanding - I say, XYZ is incorrect. Because of blah-blah. No feelings attached to it, just a fact.

"Principles of Program Analysis" covers the subject very well. It's not an easy or beginner book because it's very formal, but very complete and with plenty of examples.

The problem is what I mentioned at the end of my other comment -- complex programs will get inputs from web APIs, console, database, so it's not possible to have complete runtime coverage for all paths in the interpreter on the general case.

Something very close to this can be done and it's in fact done already by static analysis. Static analysis doesn't have any Turing complete problem. Static analysis has a limitation of providing complete answers because of the halting problem, but it can provide instead sound answers. That is, static analysis can provide all possible runtime types for any given (e.g. python) expression. This can be accomplished by doing Abstract Interpretation, or Constraint Analysis and Dataflow Analysis (kCFA), which is in way similar to what you suggest of running the program in a profiler, but instead it runs the program in an abstract value domain. With static analysis you will get some imprecisions (false positives) but no false negatives, so it can effectively be very useful for a developer in an IDE. The precision (amount of FPs) and performance are mutually dependent, but good performance and reasonable (read useful) precision can be achieved, although it's a non-trivial engineering problem.

Additionally, some programs cannot be easily and/or quickly executed to cover all possible paths, so parts of the program will remain uncovered by the profiler. That is one place where static analysis becomes very powerful, because you can cover all the program much faster (in linear time making largish precision tradeoffs, but analysis still yielding a usable result).

source: I work on a flagship static analyzer.

First of all, because it makes reviewing the code for reviewers much easier. Reviewing a messy set of random uncurated commits is really suboptimal.

Second, you might have more than one commit you want to merge to master, not just a single commit.

Also rebasing is trivial and very quick once you have done some basic reading about it.

Nobody mandates developers about their private repos and commits, nobody cares about that. This is about what gets merged into master or what gets submitted for review.

C++ syntax is Turing-complete, so due to the halting problem, finding the declaration can take a long time, potentially unlimited in pathological cases.

Absolutely not unlimited time. Once it's parsed and semantically analyzed you can find the declaration in no time. Exactly like the compiler does.

edit: to clarify, I was not taking into account unbounded recursion on template instantiation, which should be limited in any case by the compiler.

Because lots of small functions increase the system complexity by creating interdependencies. You also don't have context in each function to understand the big picture and very small functions can be meaningless on its own. You have to jump through many functions just to figure out what is being done.

Final decisions are made by totally independent folks like Linus, Greg or Andrew. They set the rules and also the community, corporate has to adapt to them or go away.

Also, what happens is that rewrites happen progressively, in small steps.

I know were you are comming from, but it was very much not the only thing. The elegant model of C pointers to model memory and CPUs are definitely good aspects of C as well as its speed, transparency and overall simplicitity.

Trying to claim that Dennis Richie's design is just bad as you constantly do in any thread mentioning C is just completely missing the point and couldn't be more wrong. One thing is pointing out problems of C and another very different is saying that the only thing going for C is UNIX.

I spend most of my time thinking what needs to be done, and how to do it. Then I make the simplest possible prototype/POC and re-check my ideas. Then I think about use cases (test cases, actually) and keep iterating making small modifications (usually) to the code and finding more edge cases. My point is, the actual amount of time actually doing code-writing is very small, most time is spent figuring things out (thinking on the high level design/algorithm), writing out the ideas as code is usually the least part of the work. Finally I do cleanups/refactor, which are quite simple to do, but take non trivial amounts of time, including writting more documentation (e.g. code comments and descriptions for the PR for reviewers and for later reference).

Technically the parsing itself and the grammar are the same, they don't depend on defined macros. Yes, defining a macro in an included file can make the resulting AST change, but the parsing algorithm doesn't really care about it.