The PIC16 and PIC18 share the same XC8 compiler, which is the HiTech compiler under the hood. It's a completely propriatary tool chain, which in itself is OK, albeit that it lacks some very basic functionality and support (like C99). Sometimes you get an error like: "cannot generate code for this expression", or the compiler will not optimize an all constant expression built from a few parameters; but instead generate software LUTs and execute the expression in runtime.
More over, the way the 8-bit PIC cores work are a pain. The hardware stack can be really limiting, both in terms of call depth and what you code can and can't do. Call the same function from inside an interrupt and in your main program? In worst cases the compiler will have to compile that function twice, because it has no fast way to "push a variable onto the stack". Stack variables are statically allocated, and the compiler does some call analysis to build a call tree and re-use RAM locations where it can. However, it assumes an interrupt can fire at any moment, and so it cannot make this assumption. Thereby it sometimes needs to create 2 separate functions which both use the variables on different RAM locations. The HiTech compiler is very specifically tailored to the PIC16/18 platform, and does the best job it can for the platform, but can't do magic and make the limitations disappear with no cost.
Having a vectored interrupt controller would make this issue even worse, especially when interrupts can be nested (which is what you want if dealing with vectored isr's & priorities). In that case the compiler would have to re-compile code for each ISR and statically allocate the stack variables for it.
Dealing with pointers is also quite slow, because there really isn't a native way to work with pointers in the instruction set. It is done with extra core registers that allow indirect RAM access. This in turn means that most pointer access on PIC16/18 will easily generate 3+ instructions worth of code, where on practically any other core (MSP430, PIC24, AVR, ARM, MIPS, etc.) this can be done in a single instruction.
So a big proportion of applications will run on a PIC16 or PIC18, but not at very fast speeds. This can be fine if all you do is read an I2C sensor and switch a relay on a threshold. But as soon you're dealing with complex protocols (the "connected world") that needs packets, data buffers, pointers, etc, code can run rather slowly. In this case I would quickly go to a PIC24 or ARM chip, because I don't have to fear these performance limitations that quickly.