Although I don't plan to bring over new assertions wholesale
into the current qualification branch, it's entirely possible
that various minor changes in main will use the new assertions;
having this basic support in the release branch will simplify that.
(This is why I'm adding the includes as a separate pass from
rewriting the individual assertions)
llvm::SmallSetVector changed semantics
(https://reviews.llvm.org/D152497) resulting in build failures in Swift.
The old semantics allowed usage of types that did not have an
`operator==` because `SmallDenseSet` uses `DenseSetInfo<T>::isEqual` to
determine equality. The new implementation switched to using
`std::find`, which internally uses `operator==`. This type is used
pretty frequently with `swift::Type`, which intentionally deletes
`operator==` as it is not the canonical type and therefore cannot be
compared in normal circumstances.
This patch adds a new type-alias to the Swift namespace that provides
the old semantic behavior for `SmallSetVector`. I've also gone through
and replaced usages of `llvm::SmallSetVector` with the
`Swift::SmallSetVector` in places where we're storing a type that
doesn't implement or explicitly deletes `operator==`. The changes to
`llvm::SmallSetVector` should improve compile-time performance, so I
left the `llvm::SmallSetVector` where possible.
Even if a class method is (or could be) overridden, it's a recursive call if it's called on the self argument:
```
class X {
// Even if foo() is overridden in a derived class, it'll end up in an
// infinite recursion if initially called on an instance of `X`.
func foo() { foo() }
}
```
rdar://89076581
The main point of this change is to make sure that a shared function always has a body: both, in the optimizer pipeline and in the swiftmodule file.
This is important because the compiler always needs to emit code for a shared function. Shared functions cannot be referenced from outside the module.
In several corner cases we missed to maintain this invariant which resulted in unresolved-symbol linker errors.
As side-effect of this change we can drop the shared_external SIL linkage and the IsSerializable flag, which simplifies the serialization and linkage concept.
The algorithm needs to take care of dead-end blocks.
This is done by propagating two flags instead of one: `reachesRecursiveCall` and `reachesFunctionExit`.
Dead-end blocks have none of both flags set.
rdar://80645083
The main change is to detect infinite recursive calls under invariant conditions. For example:
func f() {
if #available(macOS 10.4.4, *) {
f()
}
}
or invariant conditions due to forwarded arguments:
func f(_ x: Int) {
if x > 0 {
f(x)
}
}
Also, improve the warning message. Instead of giving a warning at the function location
warning: all paths through this function will call itself
give a warning at the call location:
warning: function call causes an infinite recursion
Especially in case of multiple recursive calls, it makes it easier to locate the problem.
https://bugs.swift.org/browse/SR-11842
rdar://57460599
Disregard candidates that have known override points because those
points are possible targets for dynamic dispatch. This removes a class
of false positives involving classes with known override points in
the module, as is this case in many node-based data structures.
rdar://70410948
Where possible, pass around a ClassDecl or a CanType instead of a
SILType that might wrap a metatype; the unwrapping logic was
repeated in several places.
Also add a FIXME for a bug I found by inspection.
* [SILOptimizer] Don't diagnose infinite recursion if a branch terminates the program
This patch augments the infinite recursion checker to not warn if a
branch terminates, but still warns if a branch calls into something with
@_semantics("programtermination_point"). This way, calling fatalError
doesn't disqualify you for the diagnostic, but calling exit does.
This also removes the warning workaround in the standard library, and
annotates the internal _assertionFailure functions as
programtermination_points, so they get this treatment too.
* Fix formatting in SILInstructions.cpp
* Re-add missing test
This patch augments the infinite recursion checker to not warn if a
branch terminates, but still warns if a branch calls into something with
`@_semantics("arc.programtermination_point")`. This way, calling `fatalError`
doesn't disqualify you for the diagnostic, but calling `exit` does.
This also removes the warning workaround in the standard library, and
annotates the internal _assertionFailure functions as
`programtermination_point`s, so they get this treatment too.
* [SILOptimizer] Clean up infinite recursion diagnostic pass
NFC. This cleans up the pass to avoid the extra lambda and to clear up
the ordering of what check is supposed to come before what.
* Address review comments
Add a new warning that detects when a function will call itself
recursively on all code paths. Attempts to invoke functions like this
may cause unbounded stack growth at least or undefined behavior in the
worst cases.
The detection code is implemented as DFS for a reachable exit path in
a given SILFunction.